Страницы и сортировка
Когда статей станут тысячи, отдавать их одним ответом нельзя: это медленно и не нужно. Список делят на страницы: клиент просит, например, третью страницу по 10 статей. Spring Data умеет это из коробки — достаточно добавить в метод параметр Pageable.
Контроллер
// src/main/java/com/example/blog/PostController.java
@GetMapping
public PagedModel<PostResponse> list(
@PageableDefault(size = 5, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
return new PagedModel<>(postService.findAll(pageable));
}Spring сам соберёт Pageable из параметров адреса page, size и sort. @PageableDefault задаёт значения, если параметров нет: 5 статей, новые сверху. PagedModel (из org.springframework.data.web) превращает страницу в аккуратный JSON.
Сервис
// src/main/java/com/example/blog/PostService.java
public Page<PostResponse> findAll(Pageable pageable) {
return posts.findAll(pageable).map(PostResponse::from);
}У JpaRepository уже есть findAll(Pageable). Он возвращает Page<Post> — статьи одной страницы плюс сведения о всех страницах. Метод map превращает каждую сущность в ответ и сохраняет эти сведения.
Запросы
curl "http://localhost:8080/api/posts"
curl "http://localhost:8080/api/posts?page=1&size=3&sort=title,asc"Второй запрос просит вторую страницу (нумерация с нуля) по 3 статьи, отсортированные по заголовку. Ответ (тексты статей сокращены):
{"content":[{"id":7,"title":"Oleg here",...},{"id":2,"title":"Spring Data JPA",...},{"id":4,"title":"Testing tips",...}],"page":{"size":3,"number":1,"totalElements":7,"totalPages":3}}В content — статьи страницы, в page — номер страницы, её размер, всего статей и всего страниц. По этим числам клиент рисует переключатель страниц.
Hibernate выполнил два запроса: за статьями и за их количеством:
Hibernate: select p1_0.id,p1_0.author_id,p1_0.body,p1_0.created_at,p1_0.title from posts p1_0 order by p1_0.title offset ? rows fetch first ? rows only
Hibernate: select count(p1_0.id) from posts p1_0Параметры адреса
| Параметр | Пример | Смысл |
|---|---|---|
page | page=0 | Номер страницы, с нуля |
size | size=20 | Статей на странице |
sort | sort=title,asc | Поле и направление; можно повторять: sort=author&sort=title |
Чтобы клиент не запросил миллион строк за раз, Spring ограничивает size числом 2000. Предел меняется настройкой spring.data.web.pageable.max-page-size.
Страницы в своих запросах
Pageable можно добавить последним параметром в любой метод репозитория — и в запрос по имени, и в @Query:
// src/main/java/com/example/blog/PostRepository.java
Page<Post> findByAuthorUsername(String username, Pageable pageable);Частая ошибка
Если вернуть из контроллера Page<PostResponse> напрямую, ответ придёт, но в журнале появится предупреждение:
WARN ... Serializing PageImpl instances as-is is not supported, meaning that there is no guarantee about the stability of the resulting JSON structure!
For a stable JSON structure, please use Spring Data's PagedModel (globally via @EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO))Внутреннее устройство PageImpl может поменяться в новой версии, и клиенты сломаются. Оборачивайте страницу в PagedModel, как в этом уроке.