fetchResults() - Deprecated
기존에는 fetchResults()를 사용하여 페이징 처리할 때 개발자가 별도의 count 쿼리를 작성하지 않아도 돼서 편리했다.
하지만 Querydsl 5.0 버전부터 fetchResults는 Deprecated 되었다.
fetchResults()는 개발자가 작성한 select 쿼리를 기반으로 count 용 쿼리를 내부에서 만들어서 실행한다.
단순 쿼리에서는 동작에 문제가 없었으나, 다중 그룹 쿼리(group By) 등 복잡한 쿼리에서는 잘 작동하지 않는다.
사용자들에게는 fetch() 후 결과를 count 하여 사용하라는 대안을 제시한다.
Querydsl에서 페이징 처리를 할 때 다음과 같이 보통 Pageable를 파라미터로 받고 pageSize, offset 정보를 매핑해서 정보를 가지고 온다.
public Page<Disease> findAllByCodeAndNameAndType(String code, String name, DiseaseType type, Pageable pageable) {
List<Disease> content = from(disease)
.where(searchByCode(code),
searchByName(name),
searchByType(type))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
...
반환하는 PageImpl에는 전체 데이터 수(total) 정보를 포함해야 하는데, 결과(content)로 나오는 정보는 limit 처리되었기 때문에 List의 size()를 활용할 수 없다. 그러므로 count 쿼리를 별도로 작성해서 사용해야 한다.
그런데 무조건 count를 수행하면 굳이 count 쿼리가 필요 없는 상황에도 조회가 될 수도 있다.
Spring Data에서 제공하는 PageableExecutionUtils 유틸리티 클래스의 getPage()를 사용하면 필요한 경우에만 count 쿼리를 동작시키도록 하여 최적화할 수 있다.
PageableExecutionUtils.getPage()를 살펴보자.
public static <T> Page<T> getPage(List<T> content, Pageable pageable, LongSupplier totalSupplier) {
if (pageable.isUnpaged() || pageable.getOffset() == 0) {
if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) {
return new PageImpl<>(content, pageable, content.size());
}
return new PageImpl<>(content, pageable, totalSupplier.getAsLong());
}
if (content.size() != 0 && pageable.getPageSize() > content.size()) {
return new PageImpl<>(content, pageable, pageable.getOffset() + content.size());
}
return new PageImpl<>(content, pageable, totalSupplier.getAsLong());
}
코드를 살펴보면..
1. 첫 번째 페이지이면서 content 크기가 한 페이지의 사이즈보다 작은 경우
2. 마지막 페이지일 때 즉, getOffset이 0이 아니면서, content 크기가 한 페이지의 사이즈보다 작은 경우
위 2가지 경우는 totalSupplier 대신 content의 size와 offset 값으로 total 값을 대신한다는 걸 알 수 있다.
이를 활용해서 전체 코드를 작성하면 다음과 같다.
public Page<Disease> findAllByCodeAndNameAndType(String code, String name, DiseaseType type, Pageable pageable) {
List<Disease> content = from(disease)
.where(searchByCode(code),
searchByName(name),
searchByType(type))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
JPQLQuery<Long> countQuery = from(disease)
.select(disease.count())
.where(searchByCode(code),
searchByName(name),
searchByType(type));
return PageableExecutionUtils.getPage(content, pageable, () -> countQuery.fetchOne());
}
Querydsl과 페이징을 사용할 때 PageableExecutionUtils를 사용하여 성능 이득을 노려보자.
'Spring > JPA' 카테고리의 다른 글
JPA에서 Lombok 사용시 주의할 점 (0) | 2023.12.30 |
---|---|
[JPA] PK가 아닌 필드에 Auto Increment 사용하기 (0) | 2023.12.09 |
[JPA] orphanRemoval 옵션을 사용할 때 Update 주의사항 (0) | 2023.10.28 |
@Transactional 특징, 사용방법, 주의점 (0) | 2023.08.15 |
Spring Boot 3.0 QueryDsl Maven 설정하기 (0) | 2023.04.09 |
댓글