Spring/Spring Data JPA

Spring Data JPA - 페이징과 정렬

jddng 2022. 3. 20. 18:43
728x90
반응형

 

 

페이징과 정렬

 

  • 순수 JPA 페이징과 정렬
  • 스프링 데이터 JPA 페이징과 정렬

 

 

순수 JPA의 페이징과 정렬

 

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", Member.class)
            .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();
}

 

스프링 데이터 JPA의 페이징과 정렬

 

  • 페이징과 정렬 파라미터
          - Sort : 정렬 기능
          - Pageable : 페이징 기능(내부에 Sort 포함), 구현체로 PageRequest 사용
  • 특별한 반환 타입
          - Page : count 쿼리 결과를 포함하는 페이징
          - Slice : count 쿼리 없이 다음 페이지만 확인 가능(내부적으로 limit + 1 조회)
          - List : count 쿼리 없이 결과만 반환
  • 주의: Page는 1부터 시작이 아니라 0부터 시작이다.

 

Page<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용
Slice<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByUsername(String name, Sort sort);

 

 

테스트 코드

@Test
public void page() throws Exception {
    //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));
    
    //when
    PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
    Page<Member> page = memberRepository.findByAge(10, pageRequest);
    
    //then
    List<Member> content = page.getContent(); 		//조회된 데이터
    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(); 		//다음 페이지가 있는가?
}

 

Page 인터페이스

public interface Page<T> extends Slice<T> {
    int getTotalPages(); 	//전체 페이지 수
    long getTotalElements(); 	//전체 데이터 수
    <U> Page<U> map(Function<? super T, ? extends U> converter); //변환기
}

 

Slice 인터페이스

public interface Slice<T> extends Streamable<T> {
    int getNumber();			//현재 페이지
    int getSize();			//페이지 크기
    int getNumberOfElements();		//현재 페이지에 나올 데이터 수
    List<T> getContent();		//조회된 데이터
    boolean hasContent();		//조회된 데이터 존재 여부
    Sort getSort();			//정렬 정보
    boolean isFirst();			//현재 페이지가 첫 페이지 인지 여부
    boolean isLast();			//현재 페이지가 마지막 페이지 인지 여부
    boolean hasNext();			//다음 페이지 여부
    boolean hasPrevious();		//이전 페이지 여부
    Pageable getPageable();		//페이지 요청 정보
    Pageable nextPageable();		//다음 페이지 객체
    Pageable previousPageable();	//이전 페이지 객체
    <U> Slice<U> map(Function<? super T, ? extends U> converter); //변환기
}

 

count 쿼리

 

  • count 쿼리 때문에 성능이 안 좋아질 수 있으므로 count 쿼리를 따로 작성할 수 있다.
  • 카운트 쿼리 분리(이건 복잡한 sql에서 사용, 데이터는 left join, 카운트는 left join 안 해도 됨)

 

@Query(value = “select m from Member m”,
		countQuery = “select count(m.username) from Member m”)
Page<Member> findMemberAllCountBy(Pageable pageable);

 


 

페이징을 유지하면서 Entity를 DTO로 변환하기

 

  • Page의 map 메서드를 이용하여 DTO를 변환한 후 페이징을 유지하여 새로운 Page를 반환할 수 있다.

 

Page<Member> page = memberRepository.findByAge(10, pageRequest);
Page<MemberDto> dtoPage = page.map(m -> new MemberDto());

 

728x90
반응형