-
스프링 데이터 JPA의 페이징과 정렬JPA/스프링 데이터 JPA 2023. 8. 11. 23:00
페이징과 정렬 파라미터
'org.springframework.data.domain.Sort' : 정렬 기능
'org.springframework.data.domain.Pageable' : 페이징 기능 (내부에 'Sort' 포함)
data.jpa가 아니라 data.domain이다. 즉, 어떤 SQL 언어를 사용하든 간에 정렬 방법은 같다는 것이다.
특별한 반환 타입
'org.springframework.data.domain.Page' : 추가 count 쿼리 결과를 포함하는 페이징 (totalCount 포함)
'org.springframework.data.domain.Slice' : 추가 count 쿼리 없이 다음 페이지만 확인 가능
'List' : 추가 count 쿼리 없이 결과만 반환
Page
public interface MemberRepository extends JpaRepository<Member, Long> { Page<Member> findByAge(int age, Pageable pageable); // 반환타입 Page로, Pageable은 page에 대한 정보 }
인터페이스에 findByAge라는 나이로 페이징하는 메서드를 작성한다. 이 때에 별도의 구현은 필요 없이 반환 타입을 Page로 한다. 파라미터에는 정렬하고 싶은 변수와 Pageable을 넘겨준다. Pageable은 page에 대한 정보를 주는 역할이다.
@Test public void paging() 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)); int age = 10; PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username")); // PageRequest는 pageable의 구현체, sorting은 선택이고 첫번째 두번째 파라미터만 넣어도 됨 //when Page<Member> page = memberRepository.findByAge(age, pageRequest); // 반환타입을 Page로 받으면 totalCount 쿼리도 같이 날려줌 page.map(m -> new MemberDto(member.getId(), member.getUsername(), null)); // 물론 컨트롤러로 반환할 때에는 Dto로 변환해서 반환해야 함 //then List<Member> content = page.getContent(); long totalElements = page.getTotalElements();// totalCount랑 똑같음 for (Member member : content) { System.out.println("member = " + member); } System.out.println("totalElements = " + totalElements); }
테스트 코드를 보면 findByAge가 호출될 때 정렬해야 할 age와 pageRequest를 넘겨준다.
PageRequest는 Pageable의 구현체이며, 파라미터에는 PageRequest.of(page, size, 정렬 순서, properties)를 넣어주면 되는데 첫 번째 두 번째 파라미터를 넣고 3, 4번째 파라미터의 sorting은 생략해도 된다.
그리고 Page로 쿼리를 정렬을 해주면 totalCount 쿼리도 같이 날려준다. totalElements가 totalCount인데, 페이징 되어서 짤린 데이터가 아니라 해당하는 총 데이터가 몇 개인지 알려준다.
출력을 하면 페이징 size가 3개니까 이름 순으로 3개만 가져오고 ,totalElements는 5개가 나온다.
page.getNumber(); // 페이지 번호 page.getTotalPages(); // 총 페이지 수 page.isFirst(); // 첫 번째 페이지인지 (boolean 타입) page.hasNext(); // 다음 페이지가 있는지 (boolean 타입)
페이징을 통해 다양한 정보들도 함께 가져올 수 있다. 직접 로직을 작성하는 것보다 스프링 데이터 JPA에서 지원하는 페이징 쿼리를 이용하면 훨씬 편리한 것을 알 수 있다.
하지만 totalCount()는 쓸데 없는 데이터를 너무 많이 가져올 때도 있다. 만약에 join이 많이 일어나는 상황이라면 count는 join할 필요가 없지만 join을 하고 있어서 성능 저하가 심하다. 그렇기 때문에 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);
@Query를 이용해서 countQuery는 분리하고 count를 안하는 것에는 left join을 사용하면 된다. 어차피 count는 조건도 없이 member를 count하는 것이기 때문에 분리해야지 성능이 훨씬 좋아진다.
Slice
Slice<Member> findByAge(int age, Pageable pageable); // 반환타입을 Slice로 수정
Slice를 사용하려면 인터페이스의 반환 타입을 Slice로 수정해준다.
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")); Slice<Member> page = memberRepository.findByAge(age, pageRequest); List<Member> content = page.getContent(); // long totalElements = page.getTotalElements(); Slice는 totalCount를 가져오지 않음
Slice는 모바일에서 자주 사용된다. 페이지가 넘어가는 것이 아니라 스크롤을 넘기다가 더보기를 누르면 데이터가 나오는 것을 생각하면 된다. size가 3인데 Slice는 요청한 3보다 하나 더, 즉 3+1을의 데이터를 가져와서 4번째 데이터가 있으면 더보기를 눌렀을 때 데이터가 나오고, 또 더보기를 누르면 다음꺼를 가져오고 이런 식으로 진행된다.
'JPA > 스프링 데이터 JPA' 카테고리의 다른 글
@EntityGraph - 어노테이션으로 fetch join (0) 2023.08.12 벌크성 수정 쿼리 (0) 2023.08.11 @Query - 쿼리 정의하기 (0) 2023.08.11 쿼리 메서드 - 메서드 이름으로 자동 쿼리 생성 (0) 2023.08.11 스프링 데이터 JPA의 간단한 소개 (0) 2023.08.11