Querydsl에서 동적인 쿼리를 생성해보자.
동적쿼리를 작성하는 방법은
1. BooleanBuilder를 작성하는 방법
2. Where 절과 Predicate를 이용하는 방법
3. Where 절과 피라미터로 Predicate 를 상속한 BooleanExpression을 사용하는 방법
총 세가지가 있다.
그렇다면 세가지 방법을 모두 비교해보자.
모든 코드에서 MemberSearchCondition를 파라미터로 받아 username, teamName, ageGoe, ageLoe를 조건으로 걸어 조회하고, 값이 null인경우 where절에 적용하지 않도록 했다.
@Getter
@NoArgsConstructor
public class MemberSearchCondition {
private String username;
private String teamName;
private Integer ageGoe;
private Integer ageLoe;
}
public List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition){
BooleanBuilder builder = new BooleanBuilder();
if (hasText(condition.getUsername())) {
builder.and(member.username.eq(condition.getUsername()));
}
if(hasText(condition.getTeamName())){
builder.and(team.name.eq(condition.getTeamName()));
}
if(condition.getAgeGoe() != null) {
builder.and(member.age.goe(condition.getAgeGoe()));
}
if(condition.getAgeLoe() != null){
builder.and(member.age.loe(condition.getAgeLoe()));
}
return queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team, team)
.where(builder)
.fetch();
}
if문으로 해당 필드에 값이 존재하는지 확인한 후에 필요한 부분을 BooleanBuilder에 추가하여 조회하는 방법이다. 사실 이 방법도 아래의 방법과 똑같이 BooleanExpression을 사용하고 있지만, BooleanBuilder에 한번에 모은다음 조건에 넣는다는 점이 다르다. Where문의 조건들을 한눈에 보기 어렵고, 어떤 쿼리가 나가는지 예측하기 힘들다는 단점이 있다.
public List<MemberTeamDto> searchByBooleanExpression(MemberSearchCondition condition){
return queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
team.id.as("teamId"),
team.name.as("teamName")
))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.fetch();
}
private BooleanExpression usernameEq(String username) {
return hasText(username) ? member.username.eq(username) : null;
}
private BooleanExpression teamNameEq(String teamName) {
return hasText(teamName) ? team.name.eq(teamName) : null;
}
private BooleanExpression ageGoe(Integer ageGoe) {
return ageGoe != null ? member.age.goe(ageGoe) : null;
}
private BooleanExpression ageLoe(Integer ageLoe) {
return ageLoe != null ? member.age.loe(ageLoe) : null;
}
private BooleanExpression ageBetween(Integer ageLoe, Integer ageGoe) {
return ageLoe(ageLoe).and(ageGoe(ageGoe));
}
BooleanExpression 은 and 와 or 같은 메소드들을 이용해서 BooleanExpression 을 조합해서 새로운 BooleanExpression 을 만들 수 있기 때문에 재사용성이 높고, null일경우 Where 절에서 자동으로 무시되기 때문에 안전하다는 장점이 있다. (하지만 모든 조건이 null일 경우에는 장애가 발생할 수 있으니 주의해야한다.)
그렇기 때문에 동적 쿼리를 써야할때는 BooleanExpression을 사용하는 것이 좋다고 한다.
출처:
[우아콘2020] 수십억건에서 QUERYDSL 사용하기 https://www.youtube.com/watch?v=zMAX7g6rO_Y&t=656s