
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")
.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();
}
@Test
public void paging() {
memberJpaRepository.save(new Member("user1", 10));
memberJpaRepository.save(new Member("user2", 10));
memberJpaRepository.save(new Member("user3", 10));
memberJpaRepository.save(new Member("user4", 10));
memberJpaRepository.save(new Member("user5", 10));
int age = 10;
int offset = 0;
int limit = 3;
List<Member> members= memberJpaRepository.findByPage(age, offset, limit);
long totalCount = memberJpaRepository.totalCount(age);
Assertions.assertThat(members.size()).isEqualTo(3);
Assertions.assertThat(totalCount).isEqualTo(5);
}
Spring Data JPA
- 페이징과 정렬 파라미터
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
- 총 데이터의 갯수를 알 수 있음
- 총 데이터가 필요한 곳에서 사용
- count 쿼리 사용 O
Page<Member> findByAge(int age, Pageable pageable);
memberRepository.save(new Member("user1", 10));
memberRepository.save(new Member("user2", 10));
memberRepository.save(new Member("user3", 10));
memberRepository.save(new Member("user4", 10));
memberRepository.save(new Member("user5", 10));
int age = 10;
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
Page<Member> page = memberRepository.findByAge(age, pageRequest);
List<Member> content = page.getContent();
long totalElements = page.getTotalElements();
Assertions.assertThat(content.size()).isEqualTo(3);
Assertions.assertThat(page.getTotalElements()).isEqualTo(5);
Assertions.assertThat(page.getNumber()).isEqualTo(0);
Assertions.assertThat(page.getTotalPages()).isEqualTo(2);
Assertions.assertThat(page.isFirst()).isTrue();
Assertions.assertThat(page.hasNext()).isTrue();
Slice
- 다음 Slice가 존재하는지만 알 수 있고 총 데이터의 갯수는 알 수 없음
- 총 데이터의 갯수를 알 필요가 없고 데이터가 많은 경우 사용하면 좋음
- count 쿼리 사용 X
Slice<Member> findByAge(int age, Pageable pageable);
memberRepository.save(new Member("user1", 10));
memberRepository.save(new Member("user2", 10));
memberRepository.save(new Member("user3", 10));
memberRepository.save(new Member("user4", 10));
memberRepository.save(new Member("user5", 10));
int age = 10;
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
Slice<Member> slice = memberRepository.findByAge(age, pageRequest);
Slice<MemberDto> map = slice.map(member -> new MemberDto(member.getId(), member.getUsername(), null));
List<Member> content = slice.getContent();
Assertions.assertThat(content.size()).isEqualTo(3);
Assertions.assertThat(slice.getNumber()).isEqualTo(0);
Assertions.assertThat(slice.isFirst()).isTrue();
Assertions.assertThat(slice.hasNext()).isTrue();
List
- count 쿼리 사용 X
- 콘텐츠만 받을 수 있음
List<Member> findByUsername(String name, Pageable pageable);
List<Member> findByUsername(String name, Sort sort);
count 쿼리 분리
- count 쿼리를 분리할 수 있음
- join을 이용하는 경우 count할때도 join쿼리가 나가는데 속도향상을 위해 분리하기도 함
- 하이버네이트6 부터는 의미없는 left join의 경우 join이 제거되어 나간다
@Query(value = "select m from Member m left join m.team t",
countQuery = "select count(m.username) from Member m")
Page<Member> findMemberAllCountBy(Pageable pageable);
엔티티 -> DTO 변환
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
Slice<Member> slice = memberRepository.findByAge(age, pageRequest);
Slice<MemberDto> map = slice.map(member -> new MemberDto(member.getId(), member.getUsername(), null));
인터페이스
public interface Page<T> extends Slice<T> {
int getTotalPages();
long getTotalElements();
<U> Page<U> map(Function<? super T, ? extends U> converter);
}
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);
}