Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Pages and sorting

When there are thousands of posts, returning them in one response is out of the question: it is slow and pointless. The list is split into pages: a client asks, say, for the third page of 10 posts. Spring Data does this out of the box — just add a Pageable parameter to the method.

The controller

// 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 builds the Pageable from the page, size and sort address parameters itself. @PageableDefault sets the values when the parameters are missing: 5 posts, newest first. PagedModel (from org.springframework.data.web) turns the page into tidy JSON.

The service

// src/main/java/com/example/blog/PostService.java
public Page<PostResponse> findAll(Pageable pageable) {
    return posts.findAll(pageable).map(PostResponse::from);
}

JpaRepository already has findAll(Pageable). It returns a Page<Post> — the posts of one page plus information about all pages. The map method turns each entity into a response and keeps that information.

Requests

curl "http://localhost:8080/api/posts"
curl "http://localhost:8080/api/posts?page=1&size=3&sort=title,asc"

The second request asks for the second page (numbering starts at zero) of 3 posts sorted by title. The response (post texts shortened):

{"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 holds the posts of the page, page holds the page number, its size, the total number of posts and of pages. From these numbers a client draws a page switcher.

Hibernate ran two queries: for the posts and for their count:

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

Address parameters

ParameterExampleMeaning
pagepage=0Page number, from zero
sizesize=20Posts per page
sortsort=title,ascField and direction; can repeat: sort=author&sort=title

So that a client cannot ask for a million rows at once, Spring caps size at 2000. The limit is changed with the spring.data.web.pageable.max-page-size setting.

Pages in your own queries

Pageable can be added as the last parameter of any repository method — both a name-based query and @Query:

// src/main/java/com/example/blog/PostRepository.java
Page<Post> findByAuthorUsername(String username, Pageable pageable);

Common mistake

If a controller returns Page<PostResponse> directly, the response arrives, but the log shows a warning:

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))

The internals of PageImpl may change in a new version, and clients would break. Wrap the page in PagedModel, as in this lesson.

Thanks for reading!

Donate to the site — we work hard for you.