Spring Data JPA 페이징
페이지를 구현한다면 두 가지 경우를 생각해 볼 수 있다. 스크롤이 끝에 다다르면 추가 요청으로 정보를 불러오는 무한 스크롤과 페이지 전환으로 다음 정보를 보여주는 페이지네이션이다.
둘 다 sql의 limit, offset을 잘 활용하면 구현할 수 있어 보인다. 하지만 spring data jpa에서 편리한 인터페이스를 제공한다. query method에 Pageable을 인자로 주면 return으로 Slice, Page, List 등의 타입을 받을 수 있다.
public interface S3ImageRepository extends JpaRepository<Image, Long> {
// 단순 페이징 (repository에 작성 안해도 됨)
Slice<Image> findAll(Pageable pageable);
Page<Image> findAll(Pageable pageable);
// 조건 기반 페이징
Page<Image> findByStatus(String status, Pageable pageable);
}
+)
spring data jpa는 자동으로 쿼리 메서드가 적용된다.
예를 들어 findAllByPage와 같은 메서드명을 사용한다면 쿼리 메서드 규격에 맞지 않아 No property 'page' found for type blabla 에러를 뱉는다.
페이징 처리 메서드는 별도 명명이 있진 않다.
반환 타입을 Page<T> 또는 Slice<T> 으로 지정하고 메서드 인자로 Pageable을 전달한다.
단순 페이징은 repository에 별도 작성하지 않아도 되고,
조건 기반 페이징은 repository에 작성하고 마지막 인자로 Pageable을 전달한다.
Pageable은 PageRequest를 통해 편리하게 만들 수 있다. PageRequest는 Pageable을 상속받는다.
PageRequest pageRequest = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "id"));
Slice
Slice는 (1)무한스크롤 경우에 적합하다.
select
i1_0.id,
i1_0.content,
i1_0.created_at,
i1_0.modified_at,
i1_0.pin_image_url,
i1_0.title,
i1_0.user_id
from
image i1_0
order by
i1_0.id desc limit ?,
?
findSliceBy를 실행했을 때 위와 같이 limit offset이 동작하는 걸 볼 수 있다.
boolean hasNext();
slice는 전체 개수를 조회하지 않고 limit+1로 동작하고 hasNext 메서드로 다음 데이터가 있는지 없는지를 확인할 수 있다. 무한 스크롤의 경우 다음의 존재 여부만 필요하기에 적합하다.
Page
Page는 (2)페이지네이션 구현에 적합하다.
select
i1_0.id,
i1_0.content,
i1_0.created_at,
i1_0.modified_at,
i1_0.pin_image_url,
i1_0.title,
i1_0.user_id
from
image i1_0
order by
i1_0.id desc limit ?,
?;
select
count(i1_0.id)
from
image i1_0;
findPageBy를 실행했을 때 Slice 처럼 limit offset 쿼리가 동작하고 추가로 count 집계 함수가 동작하는 걸 볼 수 있다.
public interface Page<T> extends Slice<T> {
...
/**
* Returns the number of total pages.
*
* @return the number of total pages
*/
int getTotalPages();
/**
* Returns the total amount of elements.
*
* @return the total amount of elements
*/
long getTotalElements();
....
}
Page 인터페이스는 Slice를 상속받는다. Slice의 메서드에 더해 getTotalPages, getTotalElements 같은 메서드를 사용하여 전체 페이지 수와 전체 데이터 개수를 알 수 있다. 그래서 전체 페이지 수를 알아야 하는 페이지네이션에 적합하다.
응답
public Page<Image> getImages(){
....
return Page<Image>
}
추가로 Page<T> 또는 Slice<T>로 응답할 때 추가 설정이 없어도 추가 정보들이 더해져 넘어가는 걸 볼 수 있다.
"pageable": {
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"offset": 20,
"pageNumber": 1,
"pageSize": 20,
"paged": true,
"unpaged": false
},
"last": false,
"totalElements": 1097,
"totalPages": 55,
"size": 20,
"number": 1,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"first": false,
"numberOfElements": 20,
"empty": false
위의 경우 Page<T> 응답이다.
"pageable": {
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"offset": 20,
"pageSize": 20,
"pageNumber": 1,
"paged": true,
"unpaged": false
},
"size": 20,
"number": 1,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"first": false,
"last": false,
"numberOfElements": 20,
"empty": false
위의 경우 Slice<T> 응답이다.
Page 응답에는 있는 totalElements와 totalPages 프로퍼티가 없는 것을 볼 수 있다.
사용 예시
Page<Batch> findAll(Pageable pageable);
//controller
@GetMapping("/all/batch/page")
public ResponseEntity<Page<BatchResponseDto>> getAllBatchDto(
@RequestParam("pageNumber") String pageNumber,
@RequestParam("pageSize") String pageSize,
@RequestParam("descByColumn") String descByColumn
) {
Page<BatchResponseDto> batchResponse = scheduleService.getBatchResponseByPage(pageNumber, pageSize, descByColumn);
return ResponseEntity.ok().body(batchResponse);
}
//service
public Page<BatchResponseDto> getBatchResponseByPage(String pageNumber, String pageSize, String descByColumn) {
Pageable pageable = PageRequest.of(Integer.parseInt(pageNumber), Integer.parseInt(pageSize), Sort.by(Sort.Direction.DESC, descByColumn));
Page<Batch> page = batchRepository.findAll(pageable);
return page.map(b -> batchResponseMapper.toDto(b));
}
'Spring > JPA' 카테고리의 다른 글
Spring Data JPA - Cascade, orphanRemoval 차이 (0) | 2024.09.24 |
---|---|
JDBC, JPA, Hibernate, Spring Data JPA (0) | 2023.09.28 |
스프링) JPA Entity option 지연 로딩/영속성 전이/고아 entity삭제 (23-07-12) (0) | 2023.07.14 |
스프링) JPA Entity 연관관계 - 외래 키 주인 (23-07-11) (1) | 2023.07.14 |
스프링) Spring Boot의 JPA @Transactional/SimpleJpaRepository (23-07-09) (0) | 2023.07.14 |