org.springframework.data.domain.Sort
org.springframework.data.domain.Pageable
org.springframework.data.domain.Page
org.springframework.data.domain.Slice
추가 count 쿼리 결과를 포함하는 페이징
페이지는 0번부터 시작한다.
public interface MemberRepository extends JpaRepository<Member, Long> {
// 페이징
Page<Member> findByAge(int age, Pageable pageable);
}
@SpringBootTest
@Transactional
@Rollback(false) // 롤백하지 않고 커밋하여 db에 반영 (test코드-현업 에서는 사용하면 안되지만 공부하는 단계에서만 사용)
class MemberRepositoryTest {
@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"));
//when
Page<Member> page = memberRepository.findByAge(age, 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(); //다음 페이지가 있는가?
}
}
public interface Page<T> extends Slice<T> {
int getTotalPages(); //전체 페이지 수
long getTotalElements(); //전체 데이터 수
<U> Page<U> map(Function<? super T, ? extends U> converter); //변환기
}
추가 count 쿼리 없이 다음 페이지만 확인 가능
내부적으로 limit + 1 을 조회하므로 아래 예시에서 3페이지 + 1 즉, 4페이지를 가져온다.
(다음 페이지가 있을 경우 더보기 같은 버튼으로 다음 페이지부터 다시 3 + 1 페이지를 가져오는 용도)
public interface MemberRepository extends JpaRepository<Member, Long> {
// 슬라이스
Slice<Member> findByAge(int age, Pageable pageable);
}
@SpringBootTest
@Transactional
@Rollback(false) // 롤백하지 않고 커밋하여 db에 반영 (test코드-현업 에서는 사용하면 안되지만 공부하는 단계에서만 사용)
class MemberRepositoryTest {
@Test
public void slice() 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")); // 페이지는 0번부터 시작
//when
Slice<Member> page = memberRepository.findByAge(age, 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(); //다음 페이지가 있는가?
}
}
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); //변환기
}
api 요청 파라미터로 다양한 옵션들을 추가해서 반환 받을 수 있다.
👉 page 객체에 따라 api 스펙이 달라지므로 이런 방식은 사용하면 안됨, DTO로 변환하여 반환해야 함
예) /members?page=0&size=3&sort=id,desc&sort=username,desc
page
: 현재 페이지, 0부터 시작한다.
size
: 한 페이지에 노출할 데이터 건수 (기본 20개)
sort
: 정렬 조건을 정의한다.
예) 정렬 속성,정렬 속성...(ASC | DESC), 정렬 방향을 변경하고 싶으면 sort 파라미터 추가 (asc 생략 가능)
@GetMapping("/members")
public Page<Member> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
page 객체를 controller에서 그대로 반환하면 안됨. api 스펙이 바뀌기 때문
따라서 페이지를 유지하면서 엔티티를 DTO로 변환하여 반환해야 함
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> map = page.map(member -> new MemberDto(member.getId(), member.getUsername(), member.getTeam().getName()) );
return map;
}
MemberDto 에 member을 memberDto로 변환하는 생성자를 따로 만들어 코드를 간단하게 할 수 있음
@Data
public class MemberDto {
...
public MemberDto(Member member) {
this.id = member.getId();
this.username = member.getUsername();
this.teamName = member.getTeam().getName();
}
}
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> map = page.map(member -> new MemberDto(member));
return map;
}
page 시작을 0이 아닌 1부터 시작하게 하는 등 다양하게 커스텀 할 수 있다.
@SpringBootTest
@Transactional
@Rollback(false) // 롤백하지 않고 커밋하여 db에 반영 (test코드-현업 에서는 사용하면 안되지만 공부하는 단계에서만 사용)
class MemberRepositoryTest {
@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;
// 1. PageRequest(Pageable 구현체)를 생성해서 리포지토리에 넘김
PageRequest pageRequest = PageRequest.of(0,3, Sort.by(Sort.Direction.DESC, "username"));
//when
// 2. 응답값도 Page 대신에 직접 만들어서 제공
Page<Member> page = memberRepository.findByAge(age, pageRequest);
// DTO로 변환하여 반환
Page<MemberDto> dtoPage = page.map(m -> new MemberDto(m.getId(), m.getUsername(), null));
//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(); //다음 페이지가 있는가?
}
}
@GetMapping("/members")
public Page<Member> list(@PageableDefault(size = 12, sort = "username", direction = Sort.Direction.DESC) Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
@Qualifier 에 접두사명 추가 "{접두사명}_xxx”
예 : /members?member_page=0&order_page=1
public String list(
@Qualifier("member") Pageable memberPageable,
@Qualifier("order") Pageable orderPageable, ...