{{announcement.body}}
{{announcement.title}}

Handling Pagination With JAX-RS and NoSQL in Your Jakarta EE/MicroProfile Application

DZone 's Guide to

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.

· Java Zone ·
Free Resource

As 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!

Topics:
pagination ,jakarta ee ,microprofile ,jax-rs ,nosql ,mongodb ,jakarta nosql ,java ,rest ,java api

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}