본문 바로가기

Spring/JPA

[Querydsl] Dynamic Query

이번 장에서는 Querydsl의 동적 쿼리에 대해서 알아본다.
글의 하단부에 참고한 강의와 공식문서의 경로를 첨부하였으므로 자세한 사항은 강의나 공식문서에서 확인한다.
모든 코드는 깃허브 (링크)에 올려두었다.


Querydsl에서 동적 쿼리를 사용하는 방법은 BooleanBuilder와 Where 절에 다중 파라미터를 사용하는 방법 두 가지가 있다.

BooleanBuilder

@Transactional
@SpringBootTest
@TestMethodOrder(value = OrderAnnotation.class)
public class QuerydslBasicGrammarTest {
    @Autowired
    private EntityManager entityManager;
    private JPAQueryFactory query;
    @Test
    @Order(33)
    @DisplayName("BooleanBuilder를 사용한 동적 쿼리 테스트")
    void dynamicQueryUsingBooleanBuilderTest() {
        String nameInParam = "Roy";
        Integer heightInParam = 170;
        List<SoccerPlayer> storedPlayers = searchPlayerByBooleanBuilder(nameInParam, heightInParam);

        assertEquals(1, storedPlayers.size());
        storedPlayers.forEach(player -> {
            assertEquals("Roy", player.getName());
        });
    }

    private List<SoccerPlayer> searchPlayerByBooleanBuilder(String targetName, Integer targetHeight) {
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        if (Objects.nonNull(targetName)) {
            booleanBuilder.and(soccerPlayer.name.eq(targetName));
        }
        if (Objects.nonNull(targetHeight)) {
            booleanBuilder.and(soccerPlayer.height.gt(targetHeight));
        }
        return query
                .selectFrom(soccerPlayer)
                .where(booleanBuilder)
                .fetch();
    }
}

Where State

@Transactional
@SpringBootTest
@TestMethodOrder(value = OrderAnnotation.class)
public class QuerydslBasicGrammarTest {
    @Autowired
    private EntityManager entityManager;
    private JPAQueryFactory query;
    @Test
    @Order(34)
    @DisplayName("Where절의 다중 파라미터를 사용한 동적 쿼리 테스트")
    void dynamicQueryUsingWhereStateTest() {
        String nameInParam = "Roy";
        Integer heightInParam = 170;

        List<SoccerPlayer> storedPlayers = searchPlayerByWhereState(nameInParam, heightInParam);
        assertEquals(1, storedPlayers.size());
        storedPlayers.forEach(player -> {
            assertEquals("Roy", player.getName());
        });
    }

    private List<SoccerPlayer> searchPlayerByWhereState(String targetName, Integer targetHeight) {
        return query
                .selectFrom(soccerPlayer)
                .where(
                        nameEq(targetName),
                        heightGt(targetHeight))
                .fetch();
    }

    private BooleanExpression nameEq(String targetName) {
        return isNotBlank(targetName) ? soccerPlayer.name.eq(targetName) : null;
    }

    private BooleanExpression heightGt(Integer targetHeight) {
        return nonNull(targetHeight) ? soccerPlayer.height.gt(targetHeight) : null;
    }

    private BooleanExpression weightGt(Integer targetWeight) {
        return nonNull(targetWeight) ? soccerPlayer.weight.gt(targetWeight) : null;
    }
}

Where 절의 null은 무시되기 때문에 가능한 방식이다.
이렇게 Where절에 메서드를 조합하는 방식으로 구현하는 경우 코드의 가독성과 재사용성이 높아진다.

또한 검색 조건이 변경되는 경우 메서드를 재조립하는 방식으로 수정이 가능하다.
예를 들어 위의 예제에서 검색조건은 이름은 "Roy"이고 키가 170이 넘는 선수를 찾는 쿼리이다.
이 때 검색조건이 키가 170이 넘고 몸무게가 70이 넘는 선수를 조회한다고 가정하면 아래와 같이 간단하게 수정이 가능해진다.

private List<SoccerPlayer> searchPlayerByWhereState(Integer targetHeight, Integer targetWeight) {
    return query
            .selectFrom(soccerPlayer)
            .where(
                    heightGt(targetHeight),
                    weightGt(targetWeight))
            .fetch();
}

참고한 강의:

JPA 공식 문서: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference

위키백과: https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%ED%8D%BC%EC%8B%9C%EC%8A%A4%ED%84%B4%EC%8A%A4_API

'Spring > JPA' 카테고리의 다른 글

[Querydsl] Spring Data JPA & Querydsl  (0) 2022.03.31
[Querydsl] Bulk  (0) 2022.03.31
[Querydsl] Projection  (0) 2022.03.30
[Querydsl] Sub Query  (0) 2022.03.30
[Querydsl] Join  (0) 2022.03.30