쿼리메소드 - 페이징과 정렬

박찬우·2024년 1월 11일

스프링 데이터 JPA

목록 보기
6/18

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);

// Dto 변환
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);

// Dto 변환
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); //변환기
}
profile
진짜 개발자가 되어보자

0개의 댓글