
Get 요청을 이용해 여러 건의 데이터를 가져올 때 페이징(Paging) 처리가 필요한 경우가 있습니다. Spring Data JPA에서 Page와 Pageable을 이용하고, QueryDSL을 이용하여 페이징을 사용해보겠습니다.
Querydsl 설정은 이전 글에서 확인 가능합니다.
https://velog.io/@byulcode/Spring-Boot-Querydsl-설정
public abstract class PageRequest {
private int page;
private int size;
public PageRequest() {
this.page = 1;
this.size = 10;
}
public Pageable getPageable(){
return org.springframework.data.domain.PageRequest.of(page -1, size);
}
}
public class PostRepositoryCustomImpl implements PostRepositoryCustom {
private final JPAQueryFactory queryFactory;
public PostRepositoryCustomImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
@Override
public Page<Post> findAllPostsPage(FindPostRequest request) {//PageRequest를 상속한 dto
Pageable pageable = request.getPageable();
List<Post> content = queryFactory
.selectFrom(post)
.where(
titleEq(request.getTitle())
)
.orderBy(post.createdAt.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
JPAQuery<Long> countQuery = queryFactory
.select(post.count())
.from(post)
.where(titleEq(request.getTitle()));
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}
private BooleanExpression titleEq(String title) {
return StringUtils.hasText(title) ? post.title.contains(title) : null;
}
}
(1) Count 쿼리 최적화
페이징의 리턴 타입에는 PageImpl 방식과 PageableExecutionUtils.getPage 방식이 있습니다. PageImpl 방식은 무조건 totalCount를 구하는 쿼리를 실행하게 되지만, PageableExecutionUtils.getPage 방식은 경우에 따라 생략할 수 있습니다. getPage() 메서드 내부에서 해당 람다식(fetchOne)을 호출하기 전까지 count 쿼리는 실행되지 않기 때문입니다.
(2) fetchCount() deprecated
기존에는 QueryResult<> -> fetchResults() 방식을 사용했는데, Querydsl 5.0 부터는 fetchResults()와 fetchCount()가 deprecated 되었습니다. 이는 count 쿼리가 모든 dialect에서 또는 다중 그룹 쿼리에서 완벽하게 지원되지 않기 때문에 deprecated 되었다고 합니다.
그래서 entity.count() → fetchOne()(쿼리 결과가 1개이므로) 방식으로 전체 count를 구하도록 수정했습니다.
(3) BooleanExpression
보통 동적 쿼리를 사용할 때 BooleanExpression 또는 BooleanBuilder를 사용합니다. 저는BooleanBuilder를 사용할 경우 어떤 쿼리인지 바로 판단하기 어렵다고 생각해 가독성이 더 좋은BooleanExpression을 사용했습니다.
만약 null 반환시 where 조건절에서 제외됩니다.