Overview
Este tutorial te muestra cómo implementar técnicas de paginación en una aplicación Quarkus conectada a MongoDB. Aprenderás a usar el repositorio de Jakarta Data para crear endpoints de REST API que admitan tanto métodos de paginación basados en offset como en cursor.
Pagination
La paginación es una técnica utilizada para dividir conjuntos de datos grandes en fragmentos más pequeños y manejables. Este tutorial implementa métodos de paginación basados en desplazamiento y basados en cursor. La paginación basada en compensación utiliza números de página para recuperar subconjuntos específicos de datos, mientras que la paginación basada en cursor utiliza un punto de referencia, o un cursor, para navegar por el conjunto de datos.
Tutorial
Esta guía muestra cómo realizar las siguientes acciones:
Verifique los requisitos previos
Crea un proyecto Quarkus con las dependencias necesarias
Configura la conexión de MongoDB
Define una entidad de datos y un repositorio
Implementa endpoints de REST API para paginación
Pruebe los puntos finales de paginación
Verificar los requisitos previos.
Antes de comenzar, complete las siguientes tareas previas:
Configura un clúster de MongoDB, ya sea en MongoDB Atlas o en una instancia local de Docker
Para iniciar una instancia local de MongoDB con Docker, debes ejecutar el siguiente comando:
docker run --rm -d --name mongodb-instance -p 27017:27017 mongo
Como alternativa, puede utilizar MongoDB Atlas e implementar un clúster M0 gratuito. Para obtener información sobre cómo crear una cuenta y un clúster de Atlas, consulta la guía MongoDB Primeros pasos.
Crea un proyecto de Quarkus.
Navegue hasta el Generador de código Quarkus y configure su proyecto con la siguiente configuración:
Seleccione el ID de grupo y artefacto que prefiera.
Agrega las siguientes dependencias:
JNoSQL documento MongoDB (
quarkus-jnosql-document-mongodb)RESTEasy Reactive (
quarkus-resteasy-reactive)RESTEasy Reactive Jackson (
quarkus-resteasy-reactive-jackson)OpenAPI (
quarkus-smallrye-openapi)
Genera el proyecto, descarga el archivo ZIP y extrae su contenido.
Nota
Si no puede encontrar una dependencia en el generador, agréguela manualmente al archivo pom.xml.
Después de completar la configuración, verifique que su archivo pom.xml incluya las siguientes dependencias:
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency> <dependency> <groupId>io.quarkiverse.jnosql</groupId> <artifactId>quarkus-jnosql-document-mongodb</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency> </dependencies>
Configura la base de datos de MongoDB.
Abre el archivo application.properties y añade las siguientes propiedades de configuración para conectarte a tu instancia de MongoDB:
quarkus.mongodb.connection-string = <your connection string> jnosql.document.database = fruits
Esta configuración permite que tu aplicación se conecte al clúster de MongoDB en la cadena de conexión especificada y utilice la base de datos fruits.
Importante
En los entornos de producción, habilitar el control de acceso y aplicar la autenticación. Para más información, consulta la Lista de verificación de seguridad.
Puedes anular estas propiedades utilizando variables de entorno, lo que te permite especificar diferentes configuraciones para desarrollo, pruebas y producción sin modificar tu código.
Crea una entidad de datos.
Crea una clase de entidad Fruit en el directorio src/main/java. El siguiente código define la entidad con los campos id y name:
import jakarta.nosql.Column; import jakarta.nosql.Convert; import jakarta.nosql.Entity; import jakarta.nosql.Id; import org.eclipse.jnosql.databases.mongodb.mapping.ObjectIdConverter; public class Fruit { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Fruit{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } public static Fruit of(String name) { Fruit fruit = new Fruit(); fruit.setName(name); return fruit; } }
Crea una interfaz de repositorio.
Crea una interfaz de FruitRepository que extienda la clase BasicRepository. El siguiente código define métodos tanto para la paginación basada en offset como en cursor:
import jakarta.data.Sort; import jakarta.data.page.CursoredPage; import jakarta.data.page.Page; import jakarta.data.page.PageRequest; import jakarta.data.repository.BasicRepository; import jakarta.data.repository.Find; import jakarta.data.repository.OrderBy; import jakarta.data.repository.Repository; public interface FruitRepository extends BasicRepository<Fruit, String> { CursoredPage<Fruit> cursor(PageRequest pageRequest, Sort<Fruit> order); Page<Fruit> offSet(PageRequest pageRequest); long countBy(); }
El framework implementa esta interfaz automáticamente, permitiéndote realizar operaciones en la base de datos sin escribir código repetitivo.
Crea una clase de configuración de base de datos.
Crea una clase SetupDatabase en el directorio src/main/java. El siguiente código llena la base de datos con datos de muestra al inicio y elimina los datos al apagarse:
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import io.quarkus.runtime.ShutdownEvent; import io.quarkus.runtime.StartupEvent; import org.jboss.logging.Logger; import java.util.List; public class SetupDatabase { private static final Logger LOGGER = Logger.getLogger(SetupDatabase.class.getName()); private final FruitRepository fruitRepository; public SetupDatabase(FruitRepository fruitRepository) { this.fruitRepository = fruitRepository; } void onStart( StartupEvent ev) { LOGGER.info("The application is starting..."); long count = fruitRepository.countBy(); if (count > 0) { LOGGER.info("Database already populated"); return; } List<Fruit> fruits = List.of( Fruit.of("apple"), Fruit.of("banana"), Fruit.of("cherry"), Fruit.of("date"), Fruit.of("elderberry"), Fruit.of("fig"), Fruit.of("grape"), Fruit.of("honeydew"), Fruit.of("kiwi"), Fruit.of("lemon") ); fruitRepository.saveAll(fruits); } void onStop( ShutdownEvent ev) { LOGGER.info("The application is stopping..."); fruitRepository.deleteAll(fruitRepository.findAll().toList()); } }
Crea endpoints de REST API.
Crea una clase FruitResource en el directorio src/main/java. Luego, pega el siguiente código en el archivo de clase:
import jakarta.data.Sort; import jakarta.data.page.PageRequest; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; public class FruitResource { private final FruitRepository fruitRepository; private static final Sort<Fruit> ASC = Sort.asc("name"); private static final Sort<Fruit> DESC = Sort.desc("name"); public FruitResource(FruitRepository fruitRepository) { this.fruitRepository = fruitRepository; } public Iterable<Fruit> offset( long page, int size) { var pageRequest = PageRequest.ofPage(page).size(size); return fruitRepository.offSet(pageRequest).content(); } public Iterable<Fruit> cursor( String after, String before, int size) { if (!after.isBlank()) { var pageRequest = PageRequest.ofSize(size).afterCursor(PageRequest.Cursor.forKey(after)); return fruitRepository.cursor(pageRequest, ASC).content(); } else if (!before.isBlank()) { var pageRequest = PageRequest.ofSize(size).beforeCursor(PageRequest.Cursor.forKey(before)); return fruitRepository.cursor(pageRequest, DESC).stream().toList(); } var pageRequest = PageRequest.ofSize(size); return fruitRepository.cursor(pageRequest, ASC).content(); } }
Esta clase define los siguientes puntos finales:
/fruits/offsetAdmite la paginación basada en desplazamiento mediante el uso del parámetro de querypage./fruits/cursorAdmite la paginación basada en el cursor mediante el uso de los parámetros de queryafterybefore.
Ambos extremos también aceptan el parámetro de query size para especificar el número de elementos por página.
Prueba de paginación basada en desplazamiento.
En una ventana de terminal separada, utilice los siguientes comandos curl para probar el extremo de paginación por desplazamiento. Estos comandos hacen solicitudes de diferentes páginas de datos de fruta.
Para obtener la primera página, ejecute el siguiente comando:
curl --location http://localhost:8080/fruits/offset?page=1
Para obtener la segunda página, ejecuta el siguiente comando:
curl --location http://localhost:8080/fruits/offset?page=2
Para obtener la quinta página, ejecuta el siguiente comando:
curl --location http://localhost:8080/fruits/offset?page=5
Pruebe la paginación basada en el cursor.
Utiliza los siguientes comandos curl para probar el endpoint de paginación por cursor. Estos comandos utilizan los parámetros after y before para navegar por el conjunto de datos.
Para obtener el conjunto inicial de frutas, ejecuta el siguiente comando:
curl --location http://localhost:8080/fruits/cursor
Para recuperar frutas con valores de campo name posteriores a "banana", ejecuta el siguiente comando:
curl --location http://localhost:8080/fruits/cursor?after=banana
Para obtener frutas con valores de campo name que vienen antes de "date", ejecute el siguiente comando:
curl --location http://localhost:8080/fruits/cursor?before=date
Recursos adicionales
Para saber más sobre la paginación en MongoDB, consulte la guía Paginación de Resultados en la documentación de MongoDB Atlas.
Para aprender más sobre Quarkus, consulta la documentación de Quarkus.