Querydsl 설정은 Querydsl Gradle 설정 참고
이전 글 작성할 때 테스트했던 player table로 querydsl을 적용해보자.
Player Table
QuerydslConfiguration
@Configuration
public class QuerydslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
JPAQueryFactory를 서비스에서 주입받아 사용하기 위해 Configuration을 설정해야 한다.
Entity Class 생성
player entity 클래스를 생성
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String game;
private String nickname;
private Integer rank;
}
QuerydslRepository 생성
@Repository
public class PlayerRepositorySupport extends QuerydslRepositorySupport {
private final JPAQueryFactory jpaQueryFactory;
public PlayerRepositorySupport(JPAQueryFactory jpaQueryFactory) {
super(Player.class);
this.jpaQueryFactory = jpaQueryFactory;
}
public List<Player> findAllByGame(String game) {
return jpaQueryFactory.selectFrom(player)
.where(player.game.eq(game))
.fetch();
}
}
PlayerRepositorySupport 클래스는 QuerydslRepositorySupport를 상속받고 생성자에 Bean에 등록된 JPAQueryFactory 객체를 주입받아서 사용한다.
PlayerRepositorySupport TestCode
@SpringBootTest
class PlayerTest {
@Autowired
private PlayerRepositorySupport playerRepositorySupport;
@Test
public void findAllByGame() {
List<Player> playerList = playerRepositorySupport.findAllByGame("LOL");
System.out.println("list size : " + playerList.size());
}
}
Player List Size는 5개로 game이 "LOL"인 Entity만 잘 들고 왔다.
PlayerRepositorySupport 클래스를 다시 한번 살펴보면 QuerydslRepositorySupport를 상속받다 보니 생성자에서 슈퍼 생성자에 super(Entity.class)를 등록을 해야 하는 게 불편하다. 사실 정말 중요한 건 JPAQueryFactory인데 이 객체로만 제어할 수 있지 않을까?
상속 구조와 생성자를 없애보자.
조금 더 합리적인 QuerydslRepository 생성
@RequiredArgsConstructor
@Repository
public class PlayerQueryRepository {
private final JPAQueryFactory jpaQueryFactory;
public List<Player> findAllByGame(String game) {
return jpaQueryFactory.selectFrom(QPlayer.player)
.where(QPlayer.player.game.eq(game))
.fetch();
}
}
QuerydslSupportRepository에 슈퍼 생성자에 Entity 등록이 없이 바로 JPAQueryFactory를 주입해도 무관하다.
BooleanBuilder를 사용한 동적 쿼리
public List<Player> findAllWithBooleanBuilder(String game, String nickName, Integer rank) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
if (game != null) {
booleanBuilder.and(player.game.eq(game));
}
if (nickName != null) {
booleanBuilder.and(player.nickname.eq(nickName));
}
if (rank != null) {
booleanBuilder.and(player.rank.eq(rank));
}
return jpaQueryFactory.selectFrom(player)
.where(booleanBuilder)
.fetch();
}
Querydsl을 사용하여 동적 쿼리로 where 절을 적용하려고 BooleanBuilder를 사용한다. 뭐 기능상 문제없이 잘 동작하지만 BooleanBuilder에 필드 조건들을 적용하려고 보니 각 필드들의 상태 값을 체크하고 조건을 적용해야 한다. 저 코드는 필드 3개의 대한 코드이지만 많은 필드 조건을 적용하려고 하면 코드 양이 늘어나고 조건이 한눈에 들어오지 않는다.
조금 더 합리적인 동적 쿼리 생성
public List<Player> findAllWithBooleanExpression(String game, String nickName, Integer rank) {
return jpaQueryFactory.selectFrom(player)
.where(eqGame(game), eqName(nickName), eqRank(rank))
.fetch();
}
private BooleanExpression eqGame(String game) {
return game == null ? null : player.game.eq(game);
}
private BooleanExpression eqName(String nickName) {
return nickName == null ? null : player.nickname.eq(nickName);
}
private BooleanExpression eqRank(Integer rank) {
return rank == null ? null : player.rank.eq(rank);
}
각 필드의 조건의 메서드를 별도로 생성해서 BooleanExpression 타입으로 리턴을 해주면 where 절에서는 null 값에 대해서는 조건절에서 제거가 되기 때문에 문제가 없고 조건 메서드는 다른 쿼리 메서드에서도 같이 사용할 수 있으므로 BooleanExpression으로 동적 쿼리를 생성하는 방식이 괜찮아 보인다.
다음번에는 Querydsl을 이용하여 조회 쿼리 시 성능에 영향이 미치는 점에 대해서 정리해보자.
'Spring' 카테고리의 다른 글
[Spring] Querydsl Join 성능 개선 (0) | 2022.11.21 |
---|---|
[Spring] Querydsl exists 성능 개선 (0) | 2022.11.20 |
[Spring] Querydsl Gradle 설정 (Gradle 7.X) (0) | 2022.11.18 |
[Spring] Dispatcher Servlet (0) | 2022.11.13 |
[Spring] HttpRequest부터 HttpResponse까지 동작 과정 (0) | 2022.11.12 |
댓글