This post has been featured on https://www.baeldung.com/java-weekly-330.
Often times API endpoint implementations involve retrieving data from some sort of storage. Retrieving data, even when applying a search criteria might result in hundreds, thousands or millions of records. Retrieving such amount of data could lead to performance issues, not meeting a contracted SLA, ultimately affecting the user experience.
One approach to overcome this problem is to implement pagination. You could retrieve a number of records from a data storage and add pagination links in the API response along with the page metadata back to the client application.
In a previous post, I showed readers how to include HAL hypermedia in Spring Boot RESTful applications using HATEOAS. Adding related links to REST responses help the client applications deciding what they might do next.
Some of the next actions a client application could help a customer do is to navigate through a list of resources. For instance to the first page of a result list.
- Java 8+.
- Maven 3.2+.
- Familiarity with Spring Framework.
3. HATEOAS PAGINATION IMPLEMENTATION
Adding HAL hypermedia to API responses using Spring HATEOAS, paginated or not, require the
The RESTful responses including HAL links will be sent in
application/hal+json format by default. To override this behavior you can set the
use-hal-as-default-json-media-type property to false.
The @EnableSpringDataWebSupport annotation registers a number of beans Spring MVC controllers use. They support pagination, sorting, and resources assembler injection. The latter if Spring HATEOAS is found in the classpath.
3.2. REST CONTROLLER
Let’s analyze FilmController.retrieveFilms() endpoint implementation.
Notice that Pageable pageRequest and PagedResourcesAssembler<Film> pagedResourcesAssembler arguments are injected into the method. This behavior is the result of adding @EnableSpringDataWebSupport to your application.
|pageRequest||Allows to send requests to this endpoint as
This endpoint implementation first retrieves Film JPA entities then assembles the PagedResources response. The most relevant statement related to pagination is:
How are the pagedResourcesAssembler.toResource() arguments used?
pagedResourcesAssembler.toResource() uses these arguments:
selfLink method argument: Used to add the page and size request parameters while keeping the rest of the query parameters.
For instance, for an incoming request like
/api/films?param1=value¶m2=value2&page=3&size=30, the pagination previous link would look like
If you don’t pass the selfLink,
param2=value2request parameters won’t be included.
films method argument: A Page of the Film JPA entity to be mapped to a PagedResources of Film resources. I would suggest not to expose the internal domain directly as API responses. Any change to the internal model might break the contracted API response affecting your API consumers.
filmResourceAssembler method argument: an instance of ResourceAssembler.java. pagedResourcesAssembler.toResource() internally executes the assembler.toResource() method where assembler is filmResourceAssembler. The Film Resource Assembler.toResource() method implementation uses a MapStruct-based mapper to map the domain entity Film to the API resource FilmResource. pagedResourcesAssembler.toResource() method implementation also adds a PageResource with page metadata and the pagination links to the RESTful response.
3.3. RESOURCE ASSEMBLER
You need instances of this class because pagedResourcesAssembler.toResource(), found in the REST controller implementation, takes a ResourceAssembler method argument. The library internally executes the overriden method toResource().
Instances of this class gets injected a MapStruct-based mapper toResource() method uses to map from the internal domain model to the response model.
This configuration class instantiates the filmResourceAssembler bean. Notice how you could have used the generic Java class ResourceAssembler.java to instantiate other resource assemblers.
Maybe an actorResourceAssembler:
3.4. JPA AND REST RESOURCE MODELS
The domain and response models along with the explanation for Identifiable interface and ResourceSupport base class were covered while discussing Adding HAL Links to Spring Boot applications using Spring HATEOAS.
3.5. RESOURCE MAPPER
It adds a self links to the film resource and also adds a link to each actor resource that performed in the movie.
4. SAMPLE RESPONSE PAYLOADS
The outer links array in the response payload includes references to the first, previous, current (via
"rel" : "self"), next, and last resources pages.
The content array includes all Film resources for this page. Each resource includes a nested links array with a reference to itself and an array of references to the film actors via the
"rel" : "actors" objects.
The page object provides metadata about the number of pages, current page, data set size, etc.
- The same request but accepting
This is a HAL-formatted response payload example.
The page object is like the one included in the previous example.
The outer _links object includes the navigational references to other pages.
One of the approaches to navigate through a data set is through pagination. Including HAL links in your REST API responses hints client applications what they could do next. Some of the next action a user might want to do is to navigate to the next page to book a room in a different hotel than those found in the first page.
Spring HATEOAS helps adding navigation links and page metadata. Such links and metadata in your API responses allow client applications to improve the user experience.
This tutorial helps you getting started with
spring-boot-starter-hateoas Spring Boot starter to include pagination hypermedia in your RESTful API responses.
Lets take a quick look at the Hibernate logs these sample requests generates:
Aparently there is nothing wrong here, a SQL query to retrieve film records. Notice the
offset Hibernate generates for the H2 engine. It then executes a second query, a
count SQL statement used to provide page metadata.
But Hibernate also generates these queries:
One SQL query to retrieve a language.
Hibernate executed only one SQL statement because the five films retrieved are in English, otherwise it would have generated one SQL query for each different language.
Many SQL queries to retrieve the actors for each film.
This is known as the N+1 SELECT problem. Stay tuned, I’ll cover how to avoid the N+1 SELECT problem using Spring Data JPA in a follow up post.
Thanks for reading and as always, feedback is very much appreciated. If you found this post helpful and would like to receive updates when content like this gets published, sign up to the newsletter.
7. SOURCE CODE
Accompanying source code for this blog post can be found at:
This website includes affiliate links to Amazon, Google. This means if you click an affiliate link and buy a product, I might earn a commission at no extra cost to you.