두가지 페이징 메서드를 알아볼 것인데, 

searchPageSimple() 메서드는 전체 갯수를 한번에 조회하는 방법이고,

searchPageComplex()메서드는 전체 조회와 전체 갯수를 따로 조회하는 방법이다.(가끔 카운트 쿼리가 성능을 저하시킬 수 있는 경우에 사용한다)

 

그리고 CountQuery 최적화를 위해 PageableExecutionUtils.getPage()를 이용한다 

 

1. searchPageSimple(): 전체 카운트를 한번에 조회하는 방법 

fetchResults()를 이용하면 내용과 전체 카운트를 한번에 조회할 수 있다(실제 쿼리는 2번 호출)

fetchResults()는 카운트 쿼리 실행시 필요없는 order by는 제거한다 

public class MemberRepositoryImpl implements MemberRepositoryCustom {

      private final JPAQueryFactory queryFactory;
      public MemberRepositoryImpl(EntityManager em) {
          this.queryFactory = new JPAQueryFactory(em);
} 

@Override
//회원명, 팀명, 나이(ageGoe, ageLoe)
public List<MemberTeamDto> search(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);

      private BooleanExpression usernameEq(String username) {
          return isEmpty(username) ? null : member.username.eq(username);
}

      private BooleanExpression teamNameEq(String teamName) {
          return isEmpty(teamName) ? null : team.name.eq(teamName);
}

      private BooleanExpression ageGoe(Integer ageGoe) {
          return ageGoe == null ? null : member.age.goe(ageGoe);
}

      private BooleanExpression ageLoe(Integer ageLoe) {
          return ageLoe == null ? null : member.age.loe(ageLoe);
} 
}

 

2. searchPageComplex(): 데이터 내용과 전체 카운트를 별도로 조회하는 방법 

전체 카운트를 조회할 때 조인 쿼리를 줄일 수 있다면 상당한 효과가 있다 

코드를 리팩토링해서 내용쿼리와 전체 카운트 쿼리를 읽기 좋게 분리하면 좋다 

@Override
//회원명, 팀명, 나이(ageGoe, ageLoe)
public List<MemberTeamDto> search(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);

 

3. PageableExecutions.getPage()

이거는 스프링 데이터 라이브러리가 제공하는 것으로, count 쿼리를 생략 가능한 경우 생략해서 처리하는 기능을한다.

 

-count 쿼리 생략 가능한 경우

1) 페이지 시작이면서 컨텐츠 사이즈가 페이지 사이즈가 작을 때(size(컨텐츠) < size(페이지))

2) 마지막 페이지 일 때 (offset + 컨텐츠 사이즈를 더해서 전체 사이즈를 구한다)

 

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 PageableExecutionUtils.getPage(content, pageable, countQuery::fetchCount);

 

4. 2번에 .fetchCount()는 이제 QueryDsl에서 지원이 안 될 예정이므로 count 쿼리는 따로 작성해준다 

@Test
public void count() {

    Long totalCount = queryFactory
            //.select(Wildcard.count) //select count(*)
            .select(member.count()) //select count(member.id)
            .from(member)
            .fetchOne();
    System.out.println("totalCount = " + totalCount);
 }

+ Recent posts