[JPA] QueryDSL 동적 쿼리로 검색하기

QueryDSL에서 동적 쿼리로 조회하는 방법을 남겨보겠다.


환경

  • JAVA 17
  • Spring Boot 3.14
  • QueryDSL 5.0.0

기본 테스트 코드

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
40
41
42
43
44
45
46
47
@DataJpaTest
@ActiveProfiles("test")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(QueryDslConfig.class)
class MemberRepositoryTest {
    @Autowired
    JPAQueryFactory queryFactory;

    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    private static QMember member = QMember.member;

    @BeforeEach
    public void setUp() {
        Member member1 = Member.builder()
                .userNm("Youngjae")
                .userPw("12345")
                .userRoles(AuthorityName.USER)
                .email("joyoungjae0401@gmail.com")
                .countryCode("KR")
                .emailVerifiedAt(LocalDateTime.now())
                .build();
        memberRepository.save(member1);

        Member admin1 = Member.builder()
                .userNm("Administrator")
                .userPw("abcde")
                .userRoles(AuthorityName.ADMIN)
                .email("admin@gmail.com")
                .countryCode("US")
                .emailVerifiedAt(LocalDateTime.now())
                .build();
        memberRepository.save(admin1);

        Member member2 = Member.builder()
                .userNm("test user")
                .userPw("가나다라마")
                .userRoles(AuthorityName.USER)
                .email("testUser@gmail.com")
                .countryCode("KR")
                .emailVerifiedAt(LocalDateTime.now())
                .build();
        memberRepository.save(member2);
    }
}

단일 expression 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
void getMembers() {
    // given
    ...

    // when
    BooleanExpression expression = member.emailVerifiedAt.isNotNull()
            .and(member.deletedAt.isNull())
            .and(member.userRoles.eq(AuthorityName.ADMIN));

    List<Member> members = queryFactory
            .selectFrom(member)
            .where(expression)
            .fetch();

    // then
    ...
}

다중 expression 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
void getMembers() {
    // given
    ...

    // when
    BooleanExpression expression1 = member.emailVerifiedAt.isNotNull();
    BooleanExpression expression2 = member.deletedAt.isNull();
    BooleanExpression expression3 = member.userRoles.eq(AuthorityName.ADMIN);

    List<Member> members = queryFactory
            .selectFrom(member)
            .where(expression1, expression2, expression3)
            .fetch();

    // then
    ...
}

BooleanBuilder 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
void getMembers() {
    // given
    ...

    // when
    BooleanBuilder booleanBuilder = new BooleanBuilder();
    booleanBuilder.and(member.emailVerifiedAt.isNotNull());
    booleanBuilder.and(member.deletedAt.isNull());
    booleanBuilder.and(member.userRoles.eq(AuthorityName.ADMIN));

    List<Member> members = queryFactory
            .selectFrom(member)
            .where(booleanBuilder)
            .fetch();

    // then
    ...
}

조건마다 분리해도 되고 체이닝해서 사용이 가능하기 때문에, 회사의 규칙이나 환경에 따라 사용하고 싶은 방법으로 사용하면 된다.

다만, 조건들을 조회할 때는 메서드를 만들어서 조회하는 게 좋은 것 같다.

BooleanBuilder, BooleanExpression에서는 null일 경우 해당 조건은 무시된다.
NPE 오류가 발생할 수 있기 때문에 null에 대한 처리는 유의해야 한다.


최종 예시

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
@Test
void getMembers() {
    // given
    ...

    // when
    List<Member> members = queryFactory
            .selectFrom(member)
            .where(isActiveMember(),
                    eqUserRoles(AuthorityName.USER),
                    containsEmail("youngjae"))
            .fetch();

    // then
    ...
}

private BooleanExpression containsEmail(String email) {
    return StringUtils.hasText(email) ? member.email.contains(email) : null;
}

private BooleanExpression eqUserRoles(AuthorityName authorityName) {
    return member.userRoles.eq(authorityName);
}

private BooleanExpression isNullDeletedAt() {
    return member.deletedAt.isNull();
}

private BooleanExpression isNotNullEmailVerifiedAt() {
    return member.emailVerifiedAt.isNotNull();
}

private BooleanExpression isActiveMember() {
    return isNotNullEmailVerifiedAt().and(isNullDeletedAt());
}

Categories:

Updated:

Leave a comment