Handling Pagination With JAX-RS and NoSQL in Your Jakarta EE/MicroProfile Application
Want to learn more about handling pagination with JAX-RS? Look no further than Jakarta EE and MicroProfile.
Join the DZone community and get the full member experience.
Join For FreeAs we've discussed in the previous post, "pagination is the process of separating the contents into discrete pages." It is useful in RESTful applications because we can avoid the waste of a database, server, and networking among the client and the server. This post will create a simple pagination application with JAX-RS where you can apply both Jakarta EE and MicroProfile.
This post will use MongoDB with a Car
entity to demonstrate how pagination works with the Jakarta NoSQL API.
The MongoDB database must be running while the server is up, so you can either download and install it manually or use a Docker image and then run the command:
docker run -d --name mongodb-instance -p 27017:27017 mongo
The project is a Jakarta EE and Jakarta NoSQL running through Maven with Payara Micro; thus, all configurations and dependencies are the pom.xml
file. The Jakarta NoSQL project has been approved, but it isn't a final version; therefore, the sample uses a SNAPSHOT version.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.soujava</groupId>
<artifactId>cars</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>cars-pagination-demo</name>
<url>https://soujava.org.br/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.ee.version>8.0</java.ee.version>
<java.se.version>1.8</java.se.version>
<payara.version>1.0.5</payara.version>
<version.payara.micro>5.192</version.payara.micro>
<jnosql.version>0.1.0-SNAPSHOT</jnosql.version>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>${java.ee.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jnosql.artemis</groupId>
<artifactId>artemis-document</artifactId>
<version>${jnosql.version}</version>
</dependency>
<dependency>
<groupId>org.jnosql.diana</groupId>
<artifactId>mongodb-driver</artifactId>
<version>${jnosql.version}</version>
</dependency>
</dependencies>
<build>
<finalName>cars</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.se.version}</source>
<target>${java.se.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>fish.payara.maven.plugins</groupId>
<artifactId>payara-micro-maven-plugin</artifactId>
<version>${payara.version}</version>
<configuration>
<payaraVersion>${version.payara.micro}</payaraVersion>
<autoDeployEmptyContextRoot>true</autoDeployEmptyContextRoot>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>snapshots-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
The Car
entity has five attributes: id, provider, model, year, and color.
import jakarta.nosql.mapping.Column;
import jakarta.nosql.mapping.Entity;
import jakarta.nosql.mapping.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;
@Entity
public class Car {
@Id
private Long id;
@Column
@NotBlank
private String provider;
@Column
@NotBlank
private String model;
@Column
@Positive
private Integer year;
@Column
@NotBlank
private String color;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car{" +
"id=" + id +
", provider='" + provider + '\'' +
", model='" + model + '\'' +
", year=" + year +
", color='" + color + '\'' +
'}';
}
}
Once the entity is ready, the next step is to create the CarRepository
using findAll
with pagination.
import jakarta.nosql.mapping.Pagination;
import jakarta.nosql.mapping.Repository;
import java.util.List;
public interface CarRepository extends Repository<Car, Long> {
List<Car> findAll(Pagination pagination);
}
At the Jakarta NoSQL project, we have several ways to configure the Mongo Client such as XML, JSON, YAML, properties, and programmatically. This project uses the JSON configuration.
[
{
"description":"MongoDB configuration",
"name":"mongodb",
"provider":"org.jnosql.diana.mongodb.document.MongoDBDocumentConfiguration",
"settings":{
"jakarta.nosql.host":"localhost"
}
}
]
At JAX-RS, there isn't an interface that represents pagination in a resource. Therefore, we can create a straightforward example to demonstrate the concept. The class has two attributes, one to show the page and the last one to define the size of elements of each page both from QueryParam and with validation, allowing positive value on both attributes.
import jakarta.nosql.mapping.Pagination;
import javax.validation.constraints.Positive;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
public class PageRequest {
@QueryParam("page")
@DefaultValue("1")
@Positive
private long page;
@QueryParam("size")
@DefaultValue("3")
@Positive
private long size;
public long getPage() {
return page;
}
public void setPage(long page) {
this.page = page;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public Pagination toPagination() {
return Pagination.page(page).size(size);
}
@Override
public String toString() {
return "PageRequest{" +
"page=" + page +
", size=" + size +
'}';
}
}
The last step in the code is the JAX-RS integration with the resource class.
@ApplicationPath("/resource")
public class ApplicationConfiguration extends Application {
}
import jakarta.nosql.mapping.ConfigurationUnit;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.function.Supplier;
@ApplicationScoped
@Path("cars")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CarResource {
private static final Supplier<WebApplicationException> NOT_FOUND =
() -> new WebApplicationException(Response.Status.NOT_FOUND);
@Inject
@ConfigurationUnit(database = "car")
private CarRepository repository;
@GET
public List<Car> findAll(@BeanParam @Valid PageRequest page) {
return repository.findAll(page.toPagination());
}
@GET
@Path("/{id}")
public Car findById(@PathParam("id") Long id) {
return repository.findById(id).orElseThrow(NOT_FOUND);
}
@POST
public void insert(@Valid Car car) {
repository.save(car);
}
@PUT
@Path("/{id}")
public void update(@PathParam("id") Long id, @Valid Car car) {
repository.save(car);
}
@Path("/{id}")
@DELETE
public void delete(@PathParam("id") Long id) {
repository.deleteById(id);
}
}
The ConfigurationUnit
annotation allows injecting the repository from the configuration instead of programmatically. The project is ready to launch; with Payara Micro, we can do it easily with two steps: one to compile and the second one to run the uber jar.
mvn -DskipTests clean package payara-micro:bundle
java -jar target/cars-microbundle.jar
Once the project is running, it is time to create a script to inject entities from an HTTP POST request.
curl -H "Content-Type: application/json" -X POST -d '{"id":1,"provider":"Dodge","model":"Caravan","year":2006,"color":"Teal"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":2,"provider":"Lamborghini","model":"Aventador","year":2012,"color":"Blue"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":3,"provider":"Mitsubishi","model":"Pajero","year":1997,"color":"Green"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":4,"provider":"Mazda","model":"Miata MX-5","year":2005,"color":"Mauv"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":5,"provider":"Dodge","model":"Aries","year":1981,"color":"Blue"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":6,"provider":"Ford","model":"Windstar","year":1995,"color":"Yellow"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":7,"provider":"Subaru","model":"Impreza","year":1999,"color":"Purple"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":8,"provider":"Plymouth","model":"Acclaim","year":1995,"color":"Khaki"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":9,"provider":"Audi","model":"200","year":1990,"color":"Puce"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":10,"provider":"Kia","model":"Spectra","year":2009,"color":"Crimson"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":11,"provider":"Volkswagen","model":"Golf","year":2001,"color":"Crimson"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":12,"provider":"Chevrolet","model":"Silverado 1500","year":2008,"color":"Indigo"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":13,"provider":"Studebaker","model":"Avanti","year":1961,"color":"Turquoise"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":14,"provider":"Audi","model":"100","year":1994,"color":"Purple"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":15,"provider":"Ford","model":"Taurus","year":2003,"color":"Maroon"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":16,"provider":"GMC","model":"Acadia","year":2010,"color":"Indigo"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":17,"provider":"Toyota","model":"Camry Hybrid","year":2011,"color":"Violet"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":18,"provider":"Buick","model":"Roadmaster","year":1996,"color":"Purple"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":19,"provider":"Mercury","model":"Monterey","year":2006,"color":"Mauv"}' http://localhost:8080/resource/cars
curl -H "Content-Type: application/json" -X POST -d '{"id":20,"provider":"GMC","model":"Sierra","year":2010,"color":"Violet"}' http://localhost:8080/resource/cars
We can enjoy the pagination using a few request queries:
http://localhost:8080/resource/cars
[
{
"color":"Teal",
"id":1,
"model":"Caravan",
"provider":"Dodge",
"year":2006
},
{
"color":"Blue",
"id":2,
"model":"Aventador",
"provider":"Lamborghini",
"year":2012
},
{
"color":"Green",
"id":3,
"model":"Pajero",
"provider":"Mitsubishi",
"year":1997
}
]
http://localhost:8080/resource/cars?page=2
[
{
"color":"Mauv",
"id":4,
"model":"Miata MX-5",
"provider":"Mazda",
"year":2005
},
{
"color":"Blue",
"id":5,
"model":"Aries",
"provider":"Dodge",
"year":1981
},
{
"color":"Yellow",
"id":6,
"model":"Windstar",
"provider":"Ford",
"year":1995
}
]
http://localhost:8080/resource/cars?page=4&size=5
[
{
"color":"Indigo",
"id":16,
"model":"Acadia",
"provider":"GMC",
"year":2010
},
{
"color":"Violet",
"id":17,
"model":"Camry Hybrid",
"provider":"Toyota",
"year":2011
},
{
"color":"Purple",
"id":18,
"model":"Roadmaster",
"provider":"Buick",
"year":1996
},
{
"color":"Mauv",
"id":19,
"model":"Monterey",
"provider":"Mercury",
"year":2006
},
{
"color":"Violet",
"id":20,
"model":"Sierra",
"provider":"GMC",
"year":2010
}
]
In this post, we created a simple JAX-RS that can use both Eclipse projects, Jakarta EE and MicroProfile. The code is ready, so feel free to open a Pull Request to make it even more significant. In our next post, we'll create a pagination project with Eclipse Krazo and HTML 5. Stay tuned!
Opinions expressed by DZone contributors are their own.
Comments