페이지를 구현한다면 두 가지 경우를 생각해볼 수 있다. 스크롤이 끝에 다다르면 추가 요청으로 정보를 불러오는 무한 스크롤과 페이지 전환으로 다음 정보를 보여주는 페이지네이션이다.
둘 다 sql의 limit, offset을 잘 활용하면 구현할 수 있어보인다. 하지만 spring data jpa에서 편리한 인터페이스를 제공한다. query method에 Pageable을 인자로 주면 return으로 Slice, Page, List 등의 타입을 받을 수 있다.
public interface S3ImageRepository extends JpaRepository<Image, Long> {
Slice<Image> findSliceBy(Pageable pageable);
Page<Image> findPageBy(Pageable 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 프로퍼티가 없는 것을 볼 수 있다.
사용 예시
//repository
Page<Batch> findPageBy(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.findPageBy(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 |