두가지 페이징 메서드를 알아볼 것인데,
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);
}
'QueryDSL' 카테고리의 다른 글
[Querydsl] 사용자 정의 리포지토리 (0) | 2022.03.25 |
---|---|
[Querydsl] 동적쿼리 처리하는 방법(BooleanBuilder, Where 다중 파라미터 사용) (0) | 2022.03.25 |
[Querydsl] @QueryProjection (0) | 2022.03.25 |
[Querydsl] 프로젝션(Projection) (0) | 2022.03.25 |
[Querydsl] 서브쿼리 (from절 서브쿼리 한계) (0) | 2022.03.23 |