스프링데이터 + JPA/QueryDSL

23. 동적쿼리. BooleanBuilder 방식

sdafdq 2023. 12. 1. 10:44

동적쿼리는 2가지 방식이 있음.

BooleanBuilder를 사용하는 방법과 Where문 안에 다중 파라미터를 사용하는 방법임.

 

이 시간에는 BooleanBuilder를 사용하는 방법을 알아보겠음.

 

 

우선, BooleanBuilder도 QueryDSL의 표현식을 상속받은 클래스임.

private List<Member> searchMember1(String usernameParam, Integer ageParam){
    BooleanBuilder builder = new BooleanBuilder();

    if(usernameParam != null){
        builder.and(m.username.eq(usernameParam));
    }

    if(ageParam != null){
        builder.and(m.age.eq(ageParam));
    }

    return query.selectFrom(m).where(builder)
            .fetch();
}

먼저 저렇게 BooleanBuilder라는 것을 만듦.

boolean이라기 보다는 null인지 아닌지가 더 맞다고 생각을 하는데, 여튼,

 

저렇게 null이 아닐경우에 builder.and(표현식)을 적음.

그러면, 

public BooleanBuilder and(@Nullable Predicate right) {
    if (right != null) {
        if (predicate == null) {
            predicate = right;
        } else {
            predicate = ExpressionUtils.and(predicate, right);
        }
    }
    return this;
}

이게 builder의 and 메소드인데, null일 경우 바로 넣고,

null이 아닐경우 기존에 있던 predicate와 앞서 약간 소개했던 표현식유틸에서 and로 만드는 메소드를 사용함. 즉 and로 묶어주는 거. 저거 따라가다 보면,

 

public static PredicateOperation predicate(Operator operator, List<Expression<?>> args) {
    return new PredicateOperation(operator, args);
}

결국 새로운 predicate를 저렇게 반환을 하는 건데,

저 Predicate는 표현식을 상속받았음. 즉, 그냥 표현식이라고 보면 됨.

 

private List<Member> searchMember1(String usernameParam, Integer ageParam){
    BooleanBuilder builder = new BooleanBuilder();

    if(usernameParam != null){
        builder.and(m.username.eq(usernameParam));
    }

    if(ageParam != null){
        builder.and(m.age.eq(ageParam));
    }

    return query.selectFrom(m).where(builder)
            .fetch();
}

즉 최종적으로 보면은, 

아예 모두 null 일 경우 builder의 predicate는 null일 거임.

where는

public Q where(Predicate o) {
    return queryMixin.where(o);
}

이렇게 predicate를 읽는 것이고, 

없으면 읽을 predicate가 없어서 where문 자체를 생성하지 않도록 로직을 짰을거임.

하나가 있으면 그냥 바로 builder가 가지고 있는 predicate에 넣어버릴 것이고, (기존의 predicate가 null이라면)

만약 null이 아니라면 기존의 predicate와 새로 인자로 들어온 predicate를 표현식 유틸을 사용하여 and로 묶어버릴 거임.

 

 

여튼 그렇게 표현식 자체가 완성이 되는거.

where 보면 알겠지만 그 predicate를 읽어들임.

 

 

@Test
public void dynamicQuery_BooleanBuilder(){
    String usernameParam = "member1";
    Integer ageParam = 10;

    List<Member> result = searchMember1(usernameParam, ageParam);
    assertThat(result.size()).isEqualTo(1);
}

호출해서 확인해 보면

/* select
    member1 
from
    Member member1 
where
    member1.username = ?1 
    and member1.age = ?2 */ select
        m1_0.member_id,
        m1_0.age,
        m1_0.team_id,
        m1_0.username 
    from
        member m1_0 
    where
        m1_0.username=? 
        and m1_0.age=?

둘 다 값이 있을경우 and로 묶이고,

 

@Test
public void dynamicQuery_BooleanBuilder(){
    String usernameParam = null;
    Integer ageParam = 10;

    List<Member> result = searchMember1(usernameParam, ageParam);
    assertThat(result.size()).isEqualTo(1);
}
/* select
    member1 
from
    Member member1 
where
    member1.age = ?1 */ select
        m1_0.member_id,
        m1_0.age,
        m1_0.team_id,
        m1_0.username 
    from
        member m1_0 
    where
        m1_0.age=?

하나만 있을 경우 and로 묶는 게 아니고 바로 builder 자신의 필드인 predicate에 넣기 때문에 and가 생기지 않고,

 

@Test
public void dynamicQuery_BooleanBuilder(){
    String usernameParam = null;
    Integer ageParam = null;

    List<Member> result = searchMember1(usernameParam, ageParam);
    assertThat(result.size()).isEqualTo(4);
}
/* select
    member1 
from
    Member member1 */ select
        m1_0.member_id,
        m1_0.age,
        m1_0.team_id,
        m1_0.username 
    from
        member m1_0

둘 다 null일 경우 그냥 builder의 predicate 필드가 null일 거임.

 

그럴 경우에는 where메소드에서 자신의 표현식 자체를 null로 반환할 듯.

 

builder에는 and 말고도 and, or, not 등이 있음.

 

 

'스프링데이터 + JPA > QueryDSL' 카테고리의 다른 글

25. 수정, 삭제 벌크연산  (0) 2023.12.02
24. 동적쿼리. Where 다중 파라미터  (0) 2023.12.01
22. @QueryProjection  (0) 2023.12.01
21. 프로젝션 Dto  (0) 2023.12.01
20. 프로젝션  (0) 2023.11.30