Querydsl 페이징

qufdl·2023년 6월 8일
1

Spring Boot

목록 보기
3/5
post-thumbnail

Querydsl 페이징 처리

Get 요청을 이용해 여러 건의 데이터를 가져올 때 페이징(Paging) 처리가 필요한 경우가 있습니다. Spring Data JPA에서 Page와 Pageable을 이용하고, QueryDSL을 이용하여 페이징을 사용해보겠습니다.

Querydsl 설정은 이전 글에서 확인 가능합니다.

https://velog.io/@byulcode/Spring-Boot-Querydsl-설정

Custom PageRequest

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);
    }
}
  • 처음에는 Controller 단에서 page와 size를 파라미터로 받을 수 있도록 구현했습니다. 하지만 페이징이 필요한 api마다 파라미터로 받으면 코드상 좋지 않다고 생각해 따로 PageRequest를 생성했습니다.
  • 추상 클래스로 만들어 요청 dto가 이를 상속받도록 구현했습니다.

PostRepositoryCustomImpl

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 조건절에서 제외됩니다.

0개의 댓글