BooleanBuilder / BooleanExpression 왜 궁금했을까❓
ArchiVIEW 프로젝트에서 Query DSL을 사용해 동적 쿼리를 작성하는 상황이었다. 이때, 사용되는 함수가 BooleanBuilder나 BooleanExpression을 쓰는데 차이점을 알아보고 프로젝트에 맞는 함수를 사용해보려고 한다.
BooleanBuilder와 BooleanExpression의 차이점
BooleanBuilder와 BooleanExpression은 동적 쿼리를 처리하는 함수로 둘 사이에 성능차이는 없으며 개발자 스타일에 맞게 사용하면 되는 것 같다.
- BooleanBuilder는 주로 if문으로 조건을 비교하고 BooleanBuilder에 조건을 더한다.
- BooleanExpression은 일반적으로 단일 조건에 사용되며 함수 형태로 가독성이 좋다는 장점이 있다.
BooleanBuilder
BooleanBuilder andBuilder = new BooleanBuilder();
BooleanBuilder orBuilder = new BooleanBuilder();
if(StringUtils.hasText(requestDto.getUserId())) {
andBuilder.and(reply.user.id.eq(requestDto.getUserId()));
} else {
andBuilder.and(reply.user.role.eq(Role.ROLE_MEMBER));
}
if(StringUtils.hasText(requestDto.getCompanyName())) {
andBuilder.and(question.company.name.eq(requestDto.getCompanyName()));
}
if(!requestDto.getCsList().isEmpty()) {
for(String cs : requestDto.getCsList()) {
if(StringUtils.hasText(cs)) {
orBuilder.or(csSubQuestion.csSub.name.eq(cs));
}
}
}
if(!requestDto.getJobList().isEmpty()) {
for(String js : requestDto.getJobList()) {
if(StringUtils.hasText(js)) {
orBuilder.or(jobSubQuestion.jobSub.name.eq(js));
}
}
}
- 기존의 경우 위와 같이 BooleanBuilder를 사용하여 코드를 작성했다.
- 반복적인 if문으로 인해 추후 조건이 추가된다면 if 문이 늘어나 코드의 가독성이 떨어질 것이라 생각했다.
BooleanBuilder와 BooleanExpression
private BooleanBuilder searchAndFilter(QuestionDto.SearchRequest requestDto) {
return (StringUtils.hasText(requestDto.getUserId()) ? userIdEq(requestDto.getUserId()) : userRoleEq())
.and(companyNameEq(requestDto.getCompanyName()));
}
private BooleanBuilder tagOrFilter(List<String> csList, List<String> jobList) {
BooleanBuilder builder = new BooleanBuilder();
for(String cs : csList) {
builder.or(csSubNameEq(cs));
}
for(String job : jobList) {
builder.or(jobSubNameEq(job));
}
return builder;
}
private BooleanBuilder userIdEq(String userId) {
return nullSafeBooleanBuilder(() -> reply.user.id.eq(userId));
}
private BooleanBuilder userRoleEq() {
return nullSafeBooleanBuilder(() -> reply.user.role.eq(Role.ROLE_MEMBER));
}
private BooleanBuilder companyNameEq(String companyName) {
return nullSafeBooleanBuilder(() -> question.company.name.eq(companyName));
}
private BooleanBuilder csSubNameEq(String csSubName) {
return nullSafeBooleanBuilder(() -> csSubQuestion.csSub.name.eq(csSubName));
}
private BooleanBuilder jobSubNameEq(String jobSubName) {
return nullSafeBooleanBuilder(() -> jobSubQuestion.jobSub.name.eq(jobSubName));
}
private BooleanBuilder nullSafeBooleanBuilder(Supplier<BooleanExpression> supplier) {
try {
return new BooleanBuilder(supplier.get());
} catch (IllegalArgumentException e) {
return new BooleanBuilder();
}
}
- 위와 같이 BooleanExpression을 사용하여 가독성을 높이고 BooleanExpression을 통해서 할 수 없었던 걸 BooleanBuilder로 처리하며 유연성과 재사용성을 높였다.
- searchAndFilter에서 발생할 수 있는 null을 nullSafeBooleanBuilder를 통해 처리함으로써 안정성 높일 수 있었다.