Search
〰️

Paging과 정렬

순수 JPA 페이징과 정렬

검색 조건 : 나이가 10살
정렬 조건 : 이름으로 내림차순
페이징 조건 : 첫 번째 페이지, 페이지당 보여줄 데이터는 3건
public List<Member> findByPage(int age, int offset, int limit) { return em.createQuery("select m from Member m where m.age = :age order by m.username desc") .setParameter("age", age) .setFirstResult(offset) .setMaxResults(limit) .getResultList(); } public long totalCount(int age) { return em.createQuery("select count(m) from Member m where m.age = :age", Long.class) .setParameter("age", age) .getSingleResult(); }
Java
복사

Spring Data JPA 페이징과 정렬

페이징 공통화 (DB dialect에 상관없이 interface 추상화)

페이징과 정렬 파라미터

org.springframework.data.domain.Sort : 정렬 기능
org.springframework.data.domain.Pageable : 페이징 기능 (내부에 Sort 포함)

특별한 반환 타입

org.springframework.data.domain.Page : 추가 count 쿼리 결과를 포함하는 페이징
org.springframework.data.domain.Slice : 추가 count 쿼리 없이 다음 페이지만 확인 가능 (내부적으로 limit + 1조회)
List (자바 컬렉션): 추가 count 쿼리 없이 결과만 반환

Page

MemberRepository
Page<Member> findByUsername(String name, Pageable pageable);
Java
복사
MemberResositoryTest
@Test public void paging() { //given memberRepository.save(new Member("member1", 10)); memberRepository.save(new Member("member2", 10)); memberRepository.save(new Member("member3", 10)); memberRepository.save(new Member("member4", 10)); memberRepository.save(new Member("member5", 10)); int age = 10; PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username")); //when Page<Member> page = memberRepository.findByAge(age, pageRequest); // totalCount를 가져올 필요가 없다. 반환 타입을 page로 가져오면, total count 쿼리까지 날려준다! //then List<Member> content = page.getContent(); long totalElements = page.getTotalElements(); assertThat(content.size()).isEqualTo(3); //조회된 데이터 수 assertThat(page.getTotalElements()).isEqualTo(5); //전체 데이터 수 assertThat(page.getNumber()).isEqualTo(0); //페이지 번호 assertThat(page.getTotalPages()).isEqualTo(2); //전체 페이지 번호 assertThat(page.isFirst()).isTrue(); //첫번째 항목인가? assertThat(page.hasNext()).isTrue(); //다음 페이지가 있는가? }
Java
복사
두 번째 파라미터로 받은 Pagable은 인터페이스다. 따라서 실제 사용할 때는 해당 인터페이스를 구현한 org.springframework.data.domain.PageRequest 객체를 사용한다.
PageRequest 생성자의 첫 번째 파라미터에는 현재 페이지를, 두 번째 파라미터에는 조회할 데이터 수를 입력한다. 여기에 추가로 정렬 정보도 파라미터로 사용할 수 있다. 참고로 페이지는 0부터 시작한다.

Slice

MemberRepository
Slice<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
Java
복사
MemberResositoryTest
@Test public void paging() { //given memberRepository.save(new Member("member1", 10)); memberRepository.save(new Member("member2", 10)); memberRepository.save(new Member("member3", 10)); memberRepository.save(new Member("member4", 10)); memberRepository.save(new Member("member5", 10)); int age = 10; PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username")); //when Slice<Member> page = memberRepository.findByAge(age, pageRequest); // limit+1 요청, 전체 카운트를 가져오지 않음 //then List<Member> content = page.getContent(); assertThat(content.size()).isEqualTo(3); assertThat(page.getNumber()).isEqualTo(0); assertThat(page.isFirst()).isTrue(); assertThat(page.hasNext()).isTrue(); }
Java
복사
totalCount 조회하지 않는다.
추가로 limit + 1을 조회한다. 그래서 다음 페이지 여부를 확인한다.
최근 모바일 리스트. 더보기.. 같은 경우
select member0_.member_id as member_i1_0_ , member0_.age as age2_0_ , member0_.team_id as team_id4_0_ , member0_.username as username3_0_ from member member0_ where member0_.age=10 order by member0_.username desc limit 4;
SQL
복사

List

MemberRepository
List<Member> page = memberRepository.findByAge(age, pageRequest);
Java
복사
단순 페이지별 조회

Count Query 분리

실무에서 대개 문제가 되는 Query는 totalCount 쿼리!
MemberRepository
@Query(value = "select m from Member m left join m.team t") Page<Member> findByAge(int age, Pageable pageable);
Java
복사
Paging Query
count 조회 query
최적화
→ countQuery와 pagingQuery를 분리하여 성능을 최적화한다.
MemberRepository
@Query(value = "select m from Member m left join m.team t", countQuery = "select count(m.username) from Member m") Page<Member> findByAge(int age, Pageable pageable);
Java
복사
Paging Query
count 조회 query
최적화
Paging된 Entity → DTO
Page<MemberDto> toMap = page.map(m -> new MemberDto(m.getId(), m.getUsername(), null)); // API 변환 가능한 dto 형태로 반환
Java
복사
Paging이나 Sorting같은 단순 chore 작업(JPQL)을 Spring Data JPA를 사용하여 별도 쿼리 작성없이 해결, 생산성을 높인다.
주의! page index는 1이아니라 0부터이다.