querydsl

페이징

간단한 페이징 처리

@Override
public Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition,
Pageable pageable) {
   QueryResults<MemberTeamDto> results = queryFactory
     .select(new QMemberTeamDto(
         member.id,
         member.username,
         member.age,
         team.id,
         team.name))
     .from(member)
     .leftJoin(member.team, team)
     .where(usernameEq(condition.getUsername()),
         teamNameEq(condition.getTeamName()),
         ageGoe(condition.getAgeGoe()),
         ageLoe(condition.getAgeLoe()))
     .offset(pageable.getOffset())
     .limit(pageable.getPageSize())
     .fetchResults();

   List<MemberTeamDto> content = results.getResults();
   long total = results.getTotal();

   return new PageImpl<>(content, pageable, total);
}

where의 파라미터로 동적쿼리를 구현하고 offset과 limit으로 페이징 처리를 했다.
fetchResults로 count 쿼리와 조회를 실행한다.
반환값은 Page를 구현하는 PageImpl로 넘겨준다.

count 쿼리를 분리

count 쿼리를 최적화 하기 위해서 조회를 위한 질의와 다르게 쿼리를 구성할 수도 있다.
예를 들면, count에서 join을 제거할 경우 많은 성능 향상을 기대할 수 있다.
이럴 때는 fetchResults가 아니라 fetch로 조회를 하고 count 쿼리를 카운트로만 구성한다.
다음 코드는 count와 조회를 나눈 예시이다.

@Override
public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition,
Pageable pageable) {
     List<MemberTeamDto> content = queryFactory
     .select(new QMemberTeamDto(
         member.id,
         member.username,
         member.age,
         team.id,
         team.name))
     .from(member)
     .leftJoin(member.team, team)
     .where(usernameEq(condition.getUsername()),
         teamNameEq(condition.getTeamName()),
         ageGoe(condition.getAgeGoe()),
         ageLoe(condition.getAgeLoe()))
     .offset(pageable.getOffset())
     .limit(pageable.getPageSize())
     .fetch();

     long total = queryFactory
         .select(member)
         .from(member)
         // .leftJoin(member.team, team)
         .where(usernameEq(condition.getUsername()),
             teamNameEq(condition.getTeamName()),
             ageGoe(condition.getAgeGoe()),
             ageLoe(condition.getAgeLoe()))
         .fetchCount();

     return new PageImpl<>(content, pageable, total);

카운트 자체가 필요없을 때의 최적화

JPAQuery<Member> countQuery = queryFactory
   .select(member)
   .from(member)
   .leftJoin(member.team, team)
       .where(usernameEq(condition.getUsername()),
       teamNameEq(condition.getTeamName()),
       ageGoe(condition.getAgeGoe()),
       ageLoe(condition.getAgeLoe()));

  // return new PageImpl<>(content, pageable, total);  
   return PageableExecutionUtils.getPage(content, pageable,
  countQuery::fetchCount);

반환하려는 페이지를 모두 구성할만큼 content가 부족하면 count를 몰라도 된다.
이럴 때는 count 쿼리를 따로 보내지 않아도 되기 때문에 최적화할 수 있다.

queryFactory를 fetch로 반환하지 않고 JPAQuery로 받은 다음에
필요한 경우에만 쿼리를 실행하게끔 fetchCount
PageableExecutionUtils.getPage에 넘겨주는 것이다.