Querydsl 2편 외 자잘한 것- BooleanBuilder, Booleanexpression
여태 Querydsl에서 where절에 들어올 조건을 처리하기 위해서 BooleanBuilder라는 객체를 활용해서 처리하였고, 앞서 1.5편에 이어 드디어 BooleanExpression를 활용하여 직관적으로 사용할 수 있게 되었다.
BooleanBuilder
앞서 BooleanBuilder를 간단하게 복기하고 가자면, 조건절을 단독 혹은 여러 if절을 사용하여 통해서 null를 체크하면서 거기에 and 나 or를 사용하면서 조건절을 추가한다.
그렇게 BooleanBuilder를 이용한 조건은 아래의 코드처럼 완성이 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Override
public Page<BoardOfReplyCountDTO> listOfReplySearch(String[] types,String keyword, Pageable pageable){
QBoard board = QBoard.board;
QReply reply = QReply.reply;
BooleanBuilder booleanBuilder = new BooleanBuilder();
if((types != null && types.length > 0) && keyword != null){
for(String type : types){
switch (type){
case "t":
booleanBuilder.or(board.title.contains(keyword));
break;
case "c":
booleanBuilder.or(board.content.contains(keyword));
break;
case "w":
booleanBuilder.or(board.user.contains(keyword));
break;
}
}//end for
}//end if
return applyPagination(pageable, query -> query.select(Projections.bean(
BoardOfReplyCountDTO.class,
board.bno,
board.title,
board.user,
board.regDate,
reply.count().as("replyCount")
))
.from(board)
.where(booleanBuilder,
board.bno.gt(0L)
)
.leftJoin(reply).on(reply.board.eq(board))
.groupBy(board));
}
보다시피 하나의 조건이라면 한번에 보기에는 단순하고 편하겠지만, 더욱 복잡한 프로젝트나 작업을 할 때에는 하나가 아닌 많은 조건들이 나타남에 따라 코드의 양은 위의 예시보다 늘어나는 것은 당연지사이다.
Booleanexpression
그렇다면 Booleanexpression은 어떠한 기능이고 위의 BooleanBuilder와는 어떠한 차이가 있는가? BooleanBuilder 한 메서드 안에 조건들을 다 선언하고 사용하였다면, Booleanexpression은 조건별로 메서드를 따로 빼서 where절에 넣는다.
만약 where절에 null이 반환다면 해당 조건이 무시되기에 동적 쿼리가 가능하며, 조건 메서드들은 다른 곳에서도 재사용이 가능하다.
그래도 메서드를 많이 생성하면 그 만큼 코드가 늘어날 수도 있겠지만 내가 만들고자한 메인 메서드는 한눈에 잘 볼 수 있고 조건 메서들은 따로 구분하여 볼 수 있기에 나름대로 가독성이 좋을꺼라 판단된다.
밑의 코드는 Booleanexpression를 이용해서 다시 수정한 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public Page<ListOfCountDTO> searchAll(String[] types, String keyword, Pageable pageable){
return applyPagination(pageable, query -> query.select(Projections.bean(
ListOfCountDTO.class,
board.bno,
board.title,
board.user,
board.regDate,
reply.count().as("replyCount")
)).from(board).leftJoin(reply).on(reply.board.eq(board))
.groupBy(board)
.where( titleSearch(types,keyword),
contentSearch(types,keyword),
userSearch(types,keyword),
board.bno.gt(0L))
);
}
private BooleanExpression titleSearch(String[] types, String keyword){
return Arrays.asList(types).contains("t") == true? board.title.contains(keyword) : null;
}
private BooleanExpression contentSearch(String[] types, String keyword){
return Arrays.asList(types).contains("c") == true? board.content.contains(keyword) : null;
}
private BooleanExpression userSearch(String[] types, String keyword){
return Arrays.asList(types).contains("w") == true ? board.user.contains(keyword) : null;
}
그리고 Booleanexpression를 적용해서 정상적으로 기능하는지 카테코리 타입을 작성자(“w”)이라는 조건을, 키워드는 USER100를 입력해 검색이 제대로 작동하는지 테스트 케이스를 실행해보자.
1
2
3
4
5
6
7
8
9
10
11
@Test
public void searchTest(){
String[] types = {"w"};
String keyword = "USER100";
Pageable pageable = PageRequest.of(0,10, Sort.by("bno").descending());
Page<ListOfCountDTO> list = boardRepository.searchAll(types, keyword, pageable);
list.getContent().forEach(i ->{
log.info(i);
});
}
로그에 제대로 작성자 “USER100”이 쓴 게시물이 검색이 되는 걸 볼 수 있다.
결론은..
BooleanBuilder를 오랫동안 사용했기에 익숙치 않은 Booleanexpression 를 사용하는 것은 정말로 어려웠고 심지어 이것을 굳이 써야 하는 생각이 들기도 하였다.
당연 쓰기 어려운 건 나의 실력 때문이었고 Booleanexpression의 조건을 거는 코드를 더 고심해서 구성을 했더니 Booleanexpression를 쓰는 구조가 이해가 되었다.
그렇기에 이것저것 여러 경우에 Booleanexpression왚 BooleanBuilder를 사용해서 데이터를 많이 쌓아나가야 겠다고 생각한다.
주제 밖 이야기
QuerydslRepositorySupport메서드를 새롭게 생성하여 컴파일 하니 unknown enum constant When.MAYBE라는 경고가 여러줄에 출력이 되었다.
이러한 에러가 나타난 배경은 QuerydslRepositorySupport메서드 안에 @Nullable 어노테이션을 사용하고 있었고, javax.annotation.meta.When 프로젝트 런타임에서 열거형을 사용할 수 없다는 경고였다.
이 경고를 수정하려면 JSR305 구현을 가져와야 하는데, build.gradle에 'com.google.code.findbugs:jsr305'라는 의존성을 추가하면 해결이 된다.