Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Страницы и сортировка

Когда статей станут тысячи, отдавать их одним ответом нельзя: это медленно и не нужно. Список делят на страницы: клиент просит, например, третью страницу по 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

Параметры адреса

ПараметрПримерСмысл
pagepage=0Номер страницы, с нуля
sizesize=20Статей на странице
sortsort=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, как в этом уроке.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.