티스토리 뷰
728x90
반응형
PageableExecutionUtils란?
Spring Data 라이브러리(org.springframework.data.support)에서 제공하는 헬퍼 클래스로,
Page<T> 객체(페이징 결과)를 생성할 때 불필요한 Count 쿼리 실행을 생략(Skip)하여 성능을 최적화해 줍니다.
왜 PageableExecutionUtils가 필요한가?
Spring Data JPA의 기본 페이징은 항상 2개의 쿼리를 실행
1) content 조회
2) select count(*) ...
- 마지막 페이지라면?
- 요청한 size보다 결과가 적다면?
- 더 이상 페이지가 없다는 것이 이미 명확하다면?
→ count 쿼리는 쓸데없는 비용
❌ 잘못된 방식
long count = query.fetchCount(); //무조건 count 쿼리 실행됨
List<Member> content = query.fetch();
return new PageImpl<>(content, pageable, count);
count 쿼리를 실행하지 않고도 total을 추론할 수 있으면, 실행하지 말자
| 현재 페이지 결과 | count 쿼리 실행 여부 |
| content.size < pageSize | ❌ 실행 안함 |
| 첫 페이지 & 결과 없음 | ❌ |
| 마지막 페이지가 명확 | ❌ |
| 애매한 경우 | ✅ 실행 |
✅ 올바른 방식
1) content 조회 (LIMIT/OFFSET)
2) 결과 개수 보고 마지막 페이지인지 판단
3) 마지막이면 → count 안 함
4) 아니면 → count 쿼리 실행
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;
import java.util.List;
import static com.example.entity.QMember.member;
@Repository
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final JPAQueryFactory queryFactory;
public MemberRepositoryCustomImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public Page<MemberDto> searchPage(MemberSearchCondition condition, Pageable pageable) {
// 1. 컨텐츠 조회 쿼리 (데이터를 가져옴)
List<MemberDto> content = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.where(usernameEq(condition.getUsername()),
ageEq(condition.getAge()))
.offset(pageable.getOffset()) // 페이지 번호
.limit(pageable.getPageSize()) // 페이지 사이즈
.fetch();
// 2. 카운트 쿼리 (실행하지 않고 쿼리 객체만 생성!)
// 중요: 여기서 fetch()나 fetchOne()을 호출하면 안 됩니다.
JPAQuery<Long> countQuery = queryFactory
.select(member.count())
.from(member)
.where(usernameEq(condition.getUsername()),
ageEq(condition.getAge()));
// 3. PageableExecutionUtils 사용
// 마지막 인자로 함수(Supplier)를 넘깁니다. ::fetchOne
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}
// ... Predicate 메서드 생략 ...
}
PageImpl vs PageableExecutionUtils
| 특징 | new PageImpl(...) | PageableExecutionUtils.getPage(...) |
| Count 쿼리 실행 시점 | 개발자가 미리 실행해서 **숫자(long)**로 넘겨줘야 함. | 내부 로직에 따라 필요할 때만 실행함. |
| 인자 | (List content, Pageable pageable, long total) | (List content, Pageable pageable, LongSupplier totalSupplier) |
| 성능 | 무조건 Count 쿼리가 실행됨 (비효율적일 수 있음). | 상황에 따라 Count 쿼리 생략 (최적화됨). |
| 권장 여부 | 간단한 테스트나 전체 개수가 중요하지 않을 때 사용. | 실무 프로덕션 코드에서 권장. |
728x90
반응형
'Spring > JPA' 카테고리의 다른 글
| [Querydsl] JPASQLQuery addFlag() 를 사용해서 SQL 힌트 적용 (0) | 2024.12.02 |
|---|---|
| JPA 영속성 관리 (0) | 2020.05.24 |
| JPA (Java Persistence API) (0) | 2020.05.24 |
반응형
300x250