PageableExecutionUtils는 Spring Data에서 제공하는 효율적인 페이징 처리를 도와주는 클래스입니다. 특히 getPage() 메소드는 데이터를 가져올 때 상황에 따라 최적화하여 성능에 큰 도움이 됩니다. 일단 핵심이 되는 getPage()메소드에 들어가는 인자를 분석해 보겠습니다.
public static <T> Page<T> getPage(List<T> content, Pageable pageable, LongSupplier totalSupplier) {
....
}
content: 한 페이지에 있는 데이터
pageable: 페이징 관련 정보를 담고 있는 Pageable 객체입니다. 이 객체는 현재 페이지 번호, 페이지 크기 등 여러 정보를 가지고 있습니다.
totalSupplier: 데이터의 총개수를 반환하는 람다 관련 인터페이스
핵심 로직 분석
public static <T> Page<T> getPage(List<T> content, Pageable pageable, LongSupplier totalSupplier) {
...
// 페이징 할 필요 없는 경우 == true
if (pageable.isUnpaged()) {
return new PageImpl<>(content, pageable, content.size());
}
// 한 페이지에 들어갈 데이터 개수 > 해당 페이지에 들어있는 데이터 개수인 경우
if (isPartialPage(content, pageable)) {
// 첫번째 페이지인 경우 == true
if (isFirstPage(pageable)) {
return new PageImpl<>(content, pageable, content.size());
// 첫 번째 페이지가 아니면서, 조회된 데이터가 있는 경우 == true
} else if ( !content.isEmpty()) {
return new PageImpl<>(content, pageable, pageable.getOffset() + content.size());
}
}
// 위 조건들에 해당하지 않은 경우
return new PageImpl<>(content, pageable, totalSupplier.getAsLong());
}
private static <T> boolean isPartialPage(List<T> content, Pageable pageable) {
return pageable.getPageSize() > content.size();
}
private static boolean isFirstPage(Pageable pageable) {
return pageable.getOffset() == 0;
}
}
정리하자면 PageableExecutionUtils은 상황에 따라
- 페이징이 필요 없는 경우 전체 데이터와 개수를 반환
- 현재 페이지에 들어갈 데이터 개수보다 조회된 데이터 개수가 적은 경우
- 첫 번째 페이지인지 확인하여 적절한 총 데이터 개수를 반환
- 첫 번째 페이지가 아니고 조회된 데이터가 있는 경우 pageable.getOffset() + content.size()로 반환
pageable.getOffset = pageNumber * pageSize
- 최후의 수단으로 getAsLong()을 이용해 데이터 갯수 반환
이러한 방식으로 LongSupplier를 통해 제공된 총 데이터 개수를 활용하여 불필요한 데이터 조회를 줄이고, 효율적인 페이징 처리를 가능하게 합니다.
적용 예시
@Override
public Page<UserPostDto> searchPageSimple(UserSearchCond userSearchCond, Pageable pageable) {
System.out.println("-------PageableExecutionUtils 적용 전 countQuery-----");
Long countQueryBefore = queryFactory
.select(users.count())
.from(users)
.leftJoin(users.posts, posts)
.where(
usernameEq(userSearchCond.getUsername()),
postTitleEq(userSearchCond.getPostTitle()),
ageGoe(userSearchCond.getAgeGoe()),
ageLoe(userSearchCond.getAgeLoe()))
.fetchOne();
System.out.println("-------PageableExecutionUtils 적용 후 countQuery-----");
JPAQuery<Users> countQueryAfter = queryFactory
.select(users)
.from(users)
.leftJoin(users.posts, posts)
.where(usernameEq(userSearchCond.getUsername()),
postTitleEq(userSearchCond.getPostTitle()),
ageGoe(userSearchCond.getAgeGoe()),
ageLoe(userSearchCond.getAgeLoe()));
return PageableExecutionUtils.getPage(content, pageable, countQueryAfter::fetchCount);
return new PageImpl<>(content, pageable, countQueryBefore);
}
참고자료
https://junior-datalist.tistory.com/342
[Querydsl] Pagination 성능 개선 part1.PageableExecutionUtils
목차 기존 : QueryDSL의 페이징 개선 : PageableExecutionUtils : new PageImpl()의 count 쿼리 개선 Test Case Test case 1. 페이지 사이즈 20 / 총 content 8개 / 첫 번째 페이지 호출 Test case 2. 페이지 사이즈 5 / 총 content 8
junior-datalist.tistory.com
'JAVA > JPA' 카테고리의 다른 글
[QueryDSL] fetchResults(), fetchCount() 대안 - QeuryDSL 5.0.0 버전 이후 (1) | 2025.02.05 |
---|---|
[QueryDSL] QueryDSL 문법 #4 - 동적 쿼리 (0) | 2025.02.02 |
[QueryDSL] QueryDSL 문법 #3 - DTO 반환 방법 (1) | 2025.02.01 |
[QueryDSL] Spring Data JPA + QueryDSL 기본 문법 #2 - from절에 서브쿼리 사용 문제 해결 (0) | 2025.01.30 |
[QueryDSL] Spring Data JPA + QueryDSL 설정 #1 (2) | 2025.01.22 |