spring-data-jpa 페이징

spring-data-jpa 의 페이징 기능

  • 페이징과 정렬 기능을 구현하였음
    • org.springframework.data.domain.Sort
    • org.springframework.data.domain.Pageable
  • 반환타입도 편리하게 구현하였음
    • org.springframework.data.domain.Page : count를 포함하는 페이징
    • org.springframework.data.domain.Slice : count가 없는 다음 페이지
    • List : 자바 컬렉션 리스트로 리턴할 경우 데이터만 출력함.
  • map 매서드를 통해 dto로 쉽게 변환 가능.

page와 pagable

  • Page를 리턴으로 하고 Pagable 을 매개변수로 한다.
  • Pagable에 Sort를 추가하여 정렬 기능을 넣을 수 있다.
  • page의 시작은 0이다.
Page<Member> findByAge(int age, Pageable pageable);
  • 테스트코드
@Test
void 페이징_page(){
    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;

    // 페이지는 0부터 시작한다. 
    final PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));

    final Page<Member> page = memberRepository.findByAge(age, pageRequest);

    // dto로 쉽게 반환한다.
    // json으로 변환 기능도 내부적으로 지원한다.
    final Page<MemberDto> dtos = page.map(member -> new MemberDto(member));

    for (MemberDto dto : dtos) {
        System.out.println("dto = " + dto);
    }

    final List<Member> content = page.getContent();

    final long totalCount = page.getTotalElements();
    System.out.println("totalCount = " + totalCount);

    for (Member member : content) {
        System.out.println("member = " + member);
    }

    assertThat(totalCount).isEqualTo(5);
    assertThat(content).size().isEqualTo(3);
    assertThat(page.getNumber()).isEqualTo(0);
    assertThat(page.getTotalPages()).isEqualTo(2);
    assertThat(page.isFirst()).isTrue();
    assertThat(page.hasNext()).isTrue();
}
  • 인터페이스 메서드는 findTop3ByAge 등 메서드 자체에서 페이징 처리를 지원하기도 한다. 이 경우 Pagable을 무시한다.

count query

  • Page로 리턴을 받을 때, join이 많을 경우, count query를 계산 할 때의 성능 문제가 발생할 수 있다.
  • 그 경우 count를 위한 쿼리를 구현할 수 있다. 성능 상 문제가 있을 때 직접 쿼리를 짠다.
@Query(
        value = "select m from Member m left join m.team t"
        ,countQuery = "select count(m) from Member m ")
Page<Member> findByAge(int age, Pageable pageable);
-- 엔티티를 추출할 때는 join을 사용한다.
select
    member0_.id as id1_0_,
    member0_.age as age2_0_,
    member0_.team_id as team_id4_0_,
    member0_.username as username3_0_ 
from
    member member0_ 
left outer join
    team team1_ 
        on member0_.team_id=team1_.id 
order by
    member0_.username desc limit ?

-- count를 추출할 때는 join이 없다.
select
    count(member0_.id) as col_0_0_ 
from
    member member0_

slice

  • 카운트가 없이 next 정도의 정보만을 가진 Slice 객체로 리턴한다.
  • Page가 Slice를 상속하는 형태이기 때문에, page의 몇 가지 기능을 사용하지 못한다.
Slice<Member> findByAgeAndUsernameLike(int age, Pageable pageable, String username);
select
    member0_.id as id1_0_,
    member0_.age as age2_0_,
    member0_.team_id as team_id4_0_,
    member0_.username as username3_0_ 
from
    member member0_ 
where
    member0_.age=? 
    and (
        member0_.username like ? escape ?
    ) 
order by
    member0_.username desc limit ? offset ?

-- limit 4 offset 3