개요

JPA에서 페이징을 사용하기 위해 JPQL로 작성해보면,

@Query("SELECT ue FROM UserEntity AS ue LIMIT=:limit")  
List<UserEntity> findAllByPage(@Param("limit") int limit);

문제가 발생한다. 즉, JPQL에서 LIMIT을 동적쿼리로는 사용하지 못한다는 것이다.

페이징을 가능하게 하는 객체들

1. Page

페이징된 데이터 결과 집합을 나타내는 인터페이스

  • org.springframework.data.domain.Page
  • Pageable을 파라미터로하여 가져온 결과물은 Page<T> 형태로 반환된다.
  • Page 인터페이스는 데이터 페이지와 관련된 다양한 메서드를 제공한다.
    • getCount(): 현재 페이지에 있는 엔티티 목록을 반환
    • getTotalPage(): 전체 페이지 수를 반환
    • getTotalElements(): 전체 엔티티 수 반환
    • getNumber(): 현재 페이지 번호 반환
    • getSize(): 페이지당 엔티티 수 반환

2. Pageable

데이터를 가져오는데 사용되는 페이징 및 정렬 정보를 나타내는 인터페이스

  • org.springframework.data.domain.Pageable
  • JPA에서 사용할 때 자동으로 Pageable 타입을 넘겨주면 자동으로 검색 조건을 붙여준다.
  • Controller단에서 @PageableDefault 메서드 파라미터를 사용하면 Pageable 객체에 대한 파라미터를 넘기지 않아도 자동으로 기본값을 가진 Pageable 타입 파라미터를 제공한다.
  • Pageable 객체에서는 다양한 검색 관련한 메서드를 제공한다.
    • getPageNumber(): 가져올 페이지 번호를 반환
    • getPageSize(): 페이지당 엔티티 수를 반환
    • getOffset(): 현재 페이지의 시작 위치(오프셋) 반환
    • getSort(): 데이터 정렬 정보를 나타내는 Sort 객체를 반환

3. PageRequest

PageRequest 는 Pageable 생성하기 위한 구현 클래스 중 하나이다.

  • org.springframework.data.domain.PageRequest
  • PageRequest를 사용하면 페이지 번호, 페이지 크기, 정렬 조건을 지정하여 Pageable 객체를 생성할 수 있다.
  • 주요 생성자는 아래와 같다.
    • PageRequest.of(int page, int size): 페이지 번호와 페이지당 수를 지정하여 Pageable 객체를 생성
    • PageRequest(int page, int size, Sort sort): 페이지 번호, 페이지당 수, 정렬 조건을 지정하여 pageable 객체를 생성

분석해보기

PagingAndSortingRepository

JpaRepository 인터페이스는 ListPagingAndSortingRepository를 확장하고 있으며, 그 안에는 PageingAndSortingRepository를 확장받고 있다. (PagingAndSortingRepositoryListPagingAndSortingRepositoryJpaRepository)

PagingAndSortingRepository안에는 Sort, Pageable 객체를 파라미터로 받는 메서드가 존재한다. 즉, JpaRepository를 사용할 경우, PagingAndSortingRepository를 확장받아, 페이지 및 정렬 가능하게 한다는 것이 핵심이다

활용해보기

다양한 활용 방법이 있겠지만, 직접 PageRequest를 이용한 예를 들어 정리해보려 한다.

1. Cotroller

// UserController.class

@GetMapping("/list")  
public Page<User> getUserListController(@RequestParam(defaultValue = "1") int page, 
                                        @RequestParam(defaultValue = "10") int pageSize) {  
    Pageable pages = PageRequest.of(page-1, pageSize);  
  
    return userService.getUserListService(pages));  
}
  • 페이지 번호, 페이지당 건수를 요청 파라미터로 받는다.
  • PageRequest.of를 사용하여 페이징 검색 정보를 담은 Pageable 타입인 객체를 받아 service단으로 넘긴다.
  • 컨트롤러의 response 타입은 Page 타입이다.

2. Service

// UserSerivce.class

// 2-1
public Page<User> getUserListService(Pageable pages) {  
    return userRepository.findAll(pages);  
}

// 2-2
public Page<User> getUserListService1(Pageable pages) {  
    return userRepository.findAllByJpa(pages);  
}

// 2-3
public Page<User> getUserListService2(Pageable pages) {  
    return userRepository.findAllByJpql(pages);  
}
  • 메서드 파라미터로 Pageable 타입을 받고, 해당 파라미터를 Repository로 전달한다.
  • findAll (2-1)에서는 Pageable 타입을 받아 해당 페이징 검색을 기반으로 쿼리를 변환해서 DB에 요청한다
    • findAllByJpafindAllByJpql 같은 경우는 직접 Repository에 작성해보고자 한다.

3. Repository

// UserRepository.class

// 3-2
Page<WordEntity> findAllByJpa(Pageable pages);

// 3-3
@Query("SELECT ue FROM UserEntity AS ue")  
Page<UserEntity> findAllByJpql(Pageable pages);
  • JPA나 JPQL 둘 다 사용하는데 큰 차이는 없다.
  • 추가 파라미터를 사용하더라도, Pageable의 선언 순서는 상관없다.

응답 데이터

"content": [
  {
	// ...생략
	// DB 엔티티 내용
  }
],
"pageable": {
  "sort": {
	"empty": true,
	"sorted": false,
	"unsorted": true
  },
  "offset": 0,
  "pageNumber": 0,
  "pageSize": 10,
  "paged": true,
  "unpaged": false
},
"last": true,
"totalPages": 1,
"totalElements": 1,
"size": 10,
"sort": {
  "empty": true,
  "sorted": false,
  "unsorted": true
},
"number": 0,
"first": true,
"numberOfElements": 1,
"empty": false
}

Ref :
https://velog.io/@albaneo0724/Spring-Pagination%EA%B3%BC-Page-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Pageable