Docs 菜单
Docs 主页
/ /

教程:使用Java和 Quarkus 对数据进行分页

本教程向您展示如何在连接到MongoDB 的Quarkus应用程序中实现分页技术。您学习如何使用 Jakarta 数据存储库创建同时支持基于偏移和基于游标的分页方法的 REST API 终结点。

分页是一种将大型数据集划分为更小、更易于管理的数据块的技术。 本教程实现了基于偏移基于游标的分页方法。基于偏移量的分页使用页码来检索特定的数据子集,而基于游标的分页使用参考点或游标来浏览数据集。

本教程介绍如何执行以下操作:

  • 验证先决条件

  • 创建具有所需依赖项的 Quarkus项目

  • 配置MongoDB连接

  • 定义数据实体和存储库

  • 实现用于分页的REST API终结点

  • 测试分页终结点

1

在开始之前,请完成以下先决任务:

  • 安装Java 21

  • Install Maven

  • 在MongoDB Atlas或本地Docker实例上配置MongoDB 集群

要使用Docker启动本地MongoDB实例,运行以下命令:

docker run --rm -d --name mongodb-instance -p 27017:27017 mongo

或者,您可以使用MongoDB Atlas并部署免费的 M0集群。要学习;了解如何创建Atlas帐户和集群,请参阅MongoDB入门指南

2

导航到 Quarkus Code Generator 并使用以下设置配置您的项目:

  1. 选择首选群组和工件ID。

  2. 添加以下依赖项:

    • JNoSQL 文档MongoDB (quarkus-jnosql-document-mongodb)

    • RESTEasy 响应式 (quarkus-resteasy-reactive)

    • RESTEasy Reactive Jackson (quarkus-resteasy-reactive-jackson)

    • OpenAPI (quarkus-smallrye-openapi)

  3. 生成项目,下载ZIP文件并将其解压缩。

注意

如果在生成器中找不到依赖项,请将其手动添加到 pom.xml文件中。

完成设置后,请验证您的 pom.xml文件是否包含以下依赖项:

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

打开 application.properties文件并添加以下配置属性以连接到MongoDB实例:

quarkus.mongodb.connection-string = <your connection string>
jnosql.document.database = fruits

此配置使您的应用程序能够通过指定的连接字符串连接到MongoDB 集群并使用 fruits数据库。

重要

在生产环境中,启用访问权限控制并实施身份验证。有关详细信息,请参阅安全检查清单。

您可以使用环境变量覆盖这些属性,这样您就可以为开发、测试和生产指定不同的配置,而无需修改代码。

4

src/main/java目录中创建 Fruit 实体类。以下代码定义具有 idname 字段的实体:

import jakarta.nosql.Column;
import jakarta.nosql.Convert;
import jakarta.nosql.Entity;
import jakarta.nosql.Id;
import org.eclipse.jnosql.databases.mongodb.mapping.ObjectIdConverter;
@Entity
public class Fruit {
@Id
@Convert(ObjectIdConverter.class)
private String id;
@Column
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;
}
@Override
public String toString() {
return "Fruit{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
public static Fruit of(String name) {
Fruit fruit = new Fruit();
fruit.setName(name);
return fruit;
}
}
5

创建用于扩展 BasicRepository 类的 FruitRepository 接口。以下代码定义了基于偏移和基于游标的分页方法:

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;
@Repository
public interface FruitRepository extends BasicRepository<Fruit, String> {
@Find
CursoredPage<Fruit> cursor(PageRequest pageRequest, Sort<Fruit> order);
@Find
@OrderBy("name")
Page<Fruit> offSet(PageRequest pageRequest);
long countBy();
}

框架会自动实现此接口,使您无需编写样板代码即可执行数据库操作。

6

src/main/java目录中创建一个 SetupDatabase 类。以下代码在初创企业时使用示例数据填充数据库,并在关闭时删除数据:

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;
@ApplicationScoped
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(@Observes 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(@Observes ShutdownEvent ev) {
LOGGER.info("The application is stopping...");
fruitRepository.deleteAll(fruitRepository.findAll().toList());
}
}
7

src/main/java目录中创建一个 FruitResource 类。然后,将以下代码粘贴到类文件中:

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;
@Path("/fruits")
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;
}
@Path("/offset")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Iterable<Fruit> offset(@QueryParam("page") @DefaultValue("1") long page,
@QueryParam("size") @DefaultValue("2") int size) {
var pageRequest = PageRequest.ofPage(page).size(size);
return fruitRepository.offSet(pageRequest).content();
}
@Path("/cursor")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Iterable<Fruit> cursor(@QueryParam("after") @DefaultValue("") String after,
@QueryParam("before") @DefaultValue("") String before,
@QueryParam("size") @DefaultValue("2") 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();
}
}

该类定义了以下终结点:

  • /fruits/offset:使用 page查询参数支持基于偏移量的分页。

  • /fruits/cursor:使用 afterbefore查询参数支持基于游标的分页。

两个终结点还接受 size查询参数来指定每页的列项数。

8

从项目目录运行以下命令,以在开发模式下启动 Quarkus应用程序:

./mvnw compile quarkus:dev
9

在单独的终端窗口中,使用以下 curl 命令测试偏移量分页终结点。这些命令请求不同页面的水果数据。

要获取第一页,运行以下命令:

curl --location http://localhost:8080/fruits/offset?page=1

要获取第二页,运行以下命令:

curl --location http://localhost:8080/fruits/offset?page=2

要获取第五页,运行以下命令:

curl --location http://localhost:8080/fruits/offset?page=5
10

使用以下 curl 命令测试游标分页端点。这些命令使用 afterbefore 参数浏览数据集。

要获取初始水果设立,运行以下命令:

curl --location http://localhost:8080/fruits/cursor

要获取 name字段值在 "banana" 之后的水果,运行以下命令:

curl --location http://localhost:8080/fruits/cursor?after=banana

要获取 name字段值在 "date" 之前的水果,运行以下命令:

curl --location http://localhost:8080/fruits/cursor?before=date

要学习有关MongoDB分页的更多信息,请参阅MongoDB Atlas文档中的分页结果指南。

要学习;了解有关 Quarkus 的更多信息,请参阅Quarkus 文档。

后退

Quarkus with Panache 和MongoDB

在此页面上