페이징 & 정렬

Mina Park·2022년 10월 8일
0

1. 페이징/정렬 파라미터

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

2. 특별한 반환 타입

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

3.적용 예시

  • 검색조건: 10살 이상
  • 정렬조건: 이름 내림차순
  • 페이징조건: 1페이지,페이지당 3건
public interface MemberRepository extends Repository<Member, Long> {
	Page<Member> findByAge(int age, Pageable pageable);
}
//페이징 조건과 정렬 조건 설정

@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(); //다음 페이지가 있는가?
}

Pageable 파라미터

  • 인터페이스, 따라서 실제 사용시에는 해당 인터페이스를 구현한 org.springframework.data.domain.PageRequest 객체 사용
  • PageRequest 생성자의 1번째 파라미터는 현재 페이지, 2번째 파라미터는 조회할 데이터 수 입력
  • 📌 페이지 시작은 1이 아니라 0페이지부터!!!
  • 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); //변환기
}

4. 카운트 쿼리 분리

  • 카운트의 경우 다른 테이블과의 조인이 필요없으므로 성능최적화를 위해 필요시 분리 작성
  • 복잡한 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);
  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_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id 
    order by
        member0_.username desc limit ?
 select
        count(member0_.member_id) as col_0_0_ 
    from
        member member0_

5. Top,first 사용

6. 페이지를 유지하면서 엔티티를 DTO로 변환

Page<Member> page = memberRepository.findByAge(age, pageRequest);
Page<MemberDto> toMap = page.map(member -> new MemberDto(member.getId(), member.getUsername(), null));

0개의 댓글