[Querydsl] Querydsl 페이징 연동
스프링 데이터 페이징 활용1
Querydsl 페이징 연동
- 스프링 데이터의 Page, Pageable 활용
- 데이터 내용과 전체 카운트를 별도로 조회하는 방법
fetchResults(), fetchCount()
사용자 정의 인터페이스
public interface MemberRepositoryCustom {
List<MemberTeamDto> search(MemberSearchCondition condition);
Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable); // 구현 코드 생략..
Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable);
}
데이터 내용과 전체 카운트를 별도로 조회하는 방법
/*
* 복잡한 페이징
* 데이터 조회 쿼리와 전체 카운트 쿼리를 분리
*/
public class MemberRepositoryImpl implements MemberRepositoryCustom {
...
@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
.selectFrom(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);
}
}
스프링 데이터 페이징 활용2
CountQuery 최적화
JPAQuery<Member> countQuery = queryFactory
.selectFrom(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 PageableExcutionUtils.getPage(content, pageable, countQuery::fetchCount);
- 스프링 데이터 라이브러리가 제공
- count 쿼리가 생략 가능한 경우 생략해서 처리
- 페이지 시작이면서 컨텐츠 사이즈가 페이지 사이즈보다 작을 때
- 마지막 페이지일 때(offset + 컨텐츠 사이즈를 더해서 전체 사이즈 구함)
스프링 데이터 페이징 활용3
실제 컨트롤러
@RestController
@RequiredArgsConstructor
public class MemberController {
private final MemberRepository memberRepository;
...
@GetMapping("/v3/members")
public Page<MemberTeamDto> searchMemberV3(MemberSearchCondition condition, Pageable pageable) {
return memberRepository.searchPageComplex(condition, pageable);
}
}
http://localhost:8080/v3/members?page=0&size=5
Querydsl 5.0 지원
스프링 부트 2.6부터는 Querydsl 5.0을 사용한다.
스프링 부트 2.6 이상 사용시 다음과 같은 부분을 확인해야 한다.
build.gradle
설정 변경pageableExecutionUtils
Deprecated(향후 미지원) 패키지 변경- Querydsl
fetchResults()
,fetchCount()
Deprecated(향후 미지원)
pageableExecutionUtils
pageableExecutionUtils 클래스 사용 패키지 변경
기능이 Deprecated된 것은 아니고, 사용 패키지 위치가 변경되었다. 기존 위치를 신규 위치로 변경하면 문제 없이 사용할 수 있다.
- 기존 :
org.springframework.data.repository.support.PageableExecutionUtils
- 신규 :
org.springframework.data.support.PageableExecutionUtils
Querydsl [fetchResults(), fetchCount()]
[참고]
Querydsl의fetchResult()
,fetchCount()
는 개발자가 작성한 select 쿼리를 기반으로 count용 쿼리를 내부에서 만들어서 실행한다.
그런데 이 기능은 select 구문을 단순히 count 처리하는 용도로 바꾸는 정도이기 때문에, 단순한 쿼리에서는 잘 동작하지만, 복잡한 쿼리에서는 제대로 동작하지 않는다.
Querydsl은 향후fetchResult()
,fetchCount()
를 지원하지 않기로 결정했다.
따라서, count 쿼리가 필요하면 다음과 같이 별도로 작성해야 한다.
count 쿼리 예제
@Test
void count() {
Long totalCount = queryFactory
// .select(Wildcard.count) // select count(*)
.select(member.count()) // select count(member.id)
.from(member)
.fetchOne();
}
count(*)
을 사용하고 싶다면 예제의 주석처럼Wildcard.count
를 사용하면 된다.member.count()
를 사용하면count(member.id)
로 처리된다.
위의 MemberRepositoryImpl.searchPageComplex()
예제처럼 select 쿼리와는 별도로 count 쿼리를 작성하고 fetch()
를 사용해야 한다.
수정된 searchPageComplex 예제
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();
JPAQuery<Long> countQuery = queryFactory
.select(member.count())
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe()));
return PageableExcutionUtils.getPage(content, pageable, countQuery::fetchOne);
}
<출처 : 인프런 - 실전! Querydsl(김영한)>
댓글남기기