PageRequest of
of() | 설명 |
PageRequest.of(int page, int size) | 페이지 번호(0부터 시작), 페이지당 데이터의수 |
PageRequest.of (int page, int size, Sort.Direction direction, String ...props) |
페이지 번호, 페이지당 데이터의 수, 정렬 방향, 속성(칼럼)들 |
PageRequest.of(int page, int size, Sort sort) | 페이지 번호, 페이지당 데이터의 수, 정렬방향 |
Page 객체 구성
메소드 | 설명 |
int getNumber() | 현재 페이지의 정보 |
int getSize() | 한 페이지의 크기 |
int getTotalPages | 전체 페이지의 수 |
int getNumberOfElements() | 결과 데이터 수 |
boolean hasPreviousPage() | 이전 페이지의 존재 여부 |
boolean hasNextPage() | 다음 페이지의 존재 여부 |
boolean isLastPage() | 마지막 페이지 여부 |
Pageable nextPageable() | 다음 페이지 객체 |
Pageable previousPageable() | 이전 페이지 객체 |
List<T> getContent() | 조회된 데이터 |
boolean hasContent() | 결과 존재 여부 |
Sort getSort() | 검색 시 사용된 Sort 정보 |
@RestController
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/users")
public Page<User> getAllUsers() {
PageRequest pageRequest = PageRequest.of(0, 5);
return userRepository.findAll(pageRequest);
}
@PostConstruct
public void initializing() {
for (int i = 0; i < 100; i++) {
User user = User.builder()
.username("User " + i)
.address("Korea")
.age(i)
.build();
userRepository.save(user);
}
}
}
getAllUsers() 메서드에 보면 PageRequest 객체가 존재한다.
PageRequest 객체는 Pageable 인터페이스를 상속받는다.
쉽게 Paging 을 위한 정보를 넘길 수 있는데, 이 정보에는 정렬 정보, 페이지 offset, page와 같은 정보가 담겨있다.
initializing() 을 이용해서 100명의 데이터를 추가해주고 api 호출을 해보자.
api 호출을 브라우저에서 직접 url 로 해도 좋고 curl 을 해도 좋지만 나는 Postman을 이용할 것이다.
결과
"content":[
{
"content": [
{"id": 1, "username": "User 0", "address": "Korea", "age": 0},
// 중간 생략
{"id": 5, "username": "User 4", "address": "Korea", "age": 4}
],
"pageable": {
"sort": {
"sorted": false, // 정렬 상태
"unsorted": true,
"empty": true
},
"pageSize": 5, // 한 페이지에서 나타내는 원소의 수 (게시글 수)
"pageNumber": 0, // 페이지 번호 (0번 부터 시작)
"offset": 0, // 해당 페이지에 첫 번째 원소의 수
"paged": true,
"unpaged": false
},
"totalPages": 20, // 페이지로 제공되는 총 페이지 수
"totalElements": 100, // 모든 페이지에 존재하는 총 원소 수
"last": false,
"number": 0,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"size": 5,
"numberOfElements": 5,
"first": true,
"empty": false
}
쿼리 메서드에서 페이징 사용하기
public interface UserRepository extends JpaRepository<User, Long> {
Page<User> findByAddress(String address, Pageable pageable);
}
이렇게 사용자의 주소로 조회하는 쿼리 메서드를 만들고 두 번째 파라미터로 Pageable 을 넘겨주면 된다.
그리고 컨트롤러에 가서 다시 PageRequest 를 만들어주자.
쿼리 파라미터로 넘어온 값을 페이지 정보로
@RestController
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/users")
public Page<User> getAllUserWithPageByQueryMethod(@RequestParam("page") Integer page, @RequestParam("size") Integer size) {
PageRequest pageRequest = PageRequest.of(page, size);
return userRepository.findByAddress("Korea", pageRequest);
}
@PostConstruct
public void initializing() {
for (int i = 0; i < 100; i++) {
User user = User.builder()
.username("User " + i)
.address("Korea")
.age(i)
.build();
userRepository.save(user);
}
}
}
이렇게 만들고 http://localhost:8080/users/?page=3&size=4 로 요청을 보내면 다음과 같은 결과가 출력된다.
{
"content": [
{ "id": 13, "username": "User 12", "address": "Korea", "age": 12 },
{ "id": 14, "username": "User 13", "address": "Korea", "age": 13 },
{ "id": 15, "username": "User 14", "address": "Korea", "age": 14 },
{ "id": 16, "username": "User 15", "address": "Korea", "age": 15 }
],
"pageable": {
"sort": { "sorted": false, "unsorted": true, "empty": true },
"pageNumber": 3,
"pageSize": 4,
"offset": 12,
"paged": true,
"unpaged": false
},
"totalPages": 25,
"totalElements": 100,
"last": false,
"numberOfElements": 4,
"number": 3,
"sort": { "sorted": false, "unsorted": true, "empty": true },
"size": 4,
"first": false,
"empty": false
}
반환 타입에 따른 페이징 결과
Spring Data JPA 에는 반환 타입에 따라서 각기 다른 결과를 제공한다.
- Page<T> 타입
- Slice<T> 타입
- List<T> 타입
각자 다른 결과를 반환해준다.
Page<T> 는 일반적인 게시판 형태의 페이징에서 사용된다.
여기서 중요한 정보는 총 페이지 수 이다.
그 정보를 포함하여 반환한다.
Page<T> 타입은 count 쿼리를 포함하는 페이징으로 카운트 쿼리가 자동으로 생성되어 함께 나간다.
Slice<T> 타입
Slice<T> 타입을 반환 타입으로 받게 된다면 더보기 형태의 페이징에서 사용된다.
{
"content": [
{ "id": 13, "username": "User 12", "address": "Korea", "age": 12 },
{ "id": 14, "username": "User 13", "address": "Korea", "age": 13 },
{ "id": 15, "username": "User 14", "address": "Korea", "age": 14 },
{ "id": 16, "username": "User 15", "address": "Korea", "age": 15 }
],
"pageable": {
"sort": { "sorted": false, "unsorted": true, "empty": true },
"pageNumber": 3,
"pageSize": 4,
"offset": 12,
"paged": true,
"unpaged": false
},
"number": 3,
"numberOfElements": 4,
"first": false,
"last": false,
"size": 4,
"sort": { "sorted": false, "unsorted": true, "empty": true },
"empty": false
}
Page<T> 타입의 반환에 없는 것들이 존재한다.
number과 numberOfElements 그리고 Page<T> 에 존재하던 totalPages, totalElements 가 없어졌다.
Slice<T> 타입은 추가 count 쿼리 없이 다음 페이지 확인 가능하다. 내부적으로 limit + 1 조회를 해서 totalCount 쿼리가 나가지 않아서 성능상 조금 이점을 볼 수도 있다.
List<T> 타입
@GetMapping("/users")
public List<User> getAllUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
List 반환 타입은 가장 기본적인 방법으로 count 쿼리 없이 결과만 반환한다.
Spring Web MVC 에서 더 편하게 페이징하기
Spring Data JPA의 페이징과 정렬 기능보다 훨씬 간편하게 MVC 에서 사용할 수 있게 한다.
즉, 다음과 같이 사용자가 정의한 파라미터에 따라서도 페이징이 가능하다는 소리이다.
@GetMapping("/users")
public Page<User> getAllUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
컨트롤러에서 @GetMapping 에 파리미터로 Pageable 을 추가하면 된다.
그럼 페이징 관련 쿼리가 나온다.
Springboot 내부에서 url 파라미터가 컨트롤러에 바인딩이 될 때, Pageable이 존재하면 PageRequest 객체를 생성한다.
해당 객체에서 역시 정렬도 제공하는데, url을 다음과 같이 치면 정렬과 페이징이 동시에 수행되게 할 수 있다.
- http://localhost:8080/members?page=0
- 0번 페이지 부터 20개 조회한다.
- default 가 20개로 default를 수정하는 방법도 존재한다.
- 0번 페이지 부터 20개 조회한다.
- http://localhost:8080/members?page=0&size=5
- 0번 페이지부터 5개 조회한다.
- http://localhost:8080/members?page=0&size=5&sort=id.desc
- 0번 페이지부터 5개 조회 하는데, id의 역순으로 조회한다.
'JPA' 카테고리의 다른 글
JPA - Jpa를 이용한 페이지 처리 및 검색 (0) | 2022.09.09 |
---|---|
JPA - Jpa를 이용한 RESTful API 만들기 (0) | 2022.09.08 |
JPA - Paging (0) | 2022.09.01 |
JPQL - 벌크 연산 (0) | 2022.07.10 |
JPQL - Named 쿼리 (0) | 2022.07.10 |