Using the MongoDB Client

MongoDB 是一个众所周知且被广泛使用的 NoSQL 数据库。

MongoDB is a well known NoSQL Database that is widely used.

在本指南中,我们将了解如何让 REST 服务使用 MongoDB 数据库。

In this guide, we see how you can get your REST services to use the MongoDB database.

Prerequisites

include::{includes}/prerequisites.adoc[]* 安装了 MongoDB 或安装了 Docker

Unresolved directive in mongodb.adoc - include::{includes}/prerequisites.adoc[] * MongoDB installed or Docker installed

Architecture

本指南中的应用程序非常简单:用户可以使用窗体在列表中添加元素,然后更新列表。

The application built in this guide is quite simple: the user can add elements in a list using a form and the list is updated.

浏览器和服务器之间所有信息都采用 JSON 格式。

All the information between the browser and the server is formatted as JSON.

元素存储在 MongoDB 中。

The elements are stored in MongoDB.

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

克隆 Git 存储库: git clone {quickstarts-clone-url},或下载 {quickstarts-archive-url}[存档]。

Clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive].

解决方案位于 mongodb-quickstart directory中。

The solution is located in the mongodb-quickstart directory.

Creating the Maven project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

First, we need a new project. Create a new project with the following command:

Unresolved directive in mongodb.adoc - include::{includes}/devtools/create-app.adoc[]

此命令生成一个 Maven 结构,导入 Quarkus REST(以前称为 RESTEasy Reactive)Jackson 和 MongoDB Client 扩展。在此之后,`quarkus-mongodb-client`扩展已添加到构建文件中。

This command generates a Maven structure importing the Quarkus REST (formerly RESTEasy Reactive) Jackson and MongoDB Client extensions. After this, the quarkus-mongodb-client extension has been added to your build file.

如果你已经配置了 Quarkus 项目,可以通过在项目基本目录中运行以下命令,将 `mongodb-client`扩展添加到项目:

If you already have your Quarkus project configured, you can add the mongodb-client extension to your project by running the following command in your project base directory:

Unresolved directive in mongodb.adoc - include::{includes}/devtools/extension-add.adoc[]

这会将以下内容添加到您的 pom.xml

This will add the following to your pom.xml:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-mongodb-client</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-mongodb-client")

Creating your first JSON REST service

在此示例中,我们将创建一个应用程序来管理水果列表。

In this example, we will create an application to manage a list of fruits.

首先,我们按如下方式创建 `Fruit`bean:

First, let’s create the Fruit bean as follows:

package org.acme.mongodb;

import java.util.Objects;

public class Fruit {

    private String name;
    private String description;
    private String id;

    public Fruit() {
    }

    public Fruit(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Fruit)) {
            return false;
        }

        Fruit other = (Fruit) obj;

        return Objects.equals(other.name, this.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.name);
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}

没什么新奇之处。需要特别注意的是,JSON 序列化层要求有缺省构造函数。

Nothing fancy. One important thing to note is that having a default constructor is required by the JSON serialization layer.

现在,创建一个 org.acme.mongodb.FruitService,它将成为应用程序的业务层,并存储/加载来自 mongoDB 数据库的水果。

Now create a org.acme.mongodb.FruitService that will be the business layer of our application and store/load the fruits from the mongoDB database.

package org.acme.mongodb;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@ApplicationScoped
public class FruitService {

    @Inject MongoClient mongoClient;

    public List<Fruit> list(){
        List<Fruit> list = new ArrayList<>();
        MongoCursor<Document> cursor = getCollection().find().iterator();

        try {
            while (cursor.hasNext()) {
                Document document = cursor.next();
                Fruit fruit = new Fruit();
                fruit.setName(document.getString("name"));
                fruit.setDescription(document.getString("description"));
                list.add(fruit);
            }
        } finally {
            cursor.close();
        }
        return list;
    }

    public void add(Fruit fruit){
        Document document = new Document()
                .append("name", fruit.getName())
                .append("description", fruit.getDescription());
        getCollection().insertOne(document);
    }

    private MongoCollection getCollection(){
        return mongoClient.getDatabase("fruit").getCollection("fruit");
    }
}

现在,创建 `org.acme.mongodb.FruitResource`类,如下所示:

Now, create the org.acme.mongodb.FruitResource class as follows:

@Path("/fruits")
public class FruitResource {

    @Inject FruitService fruitService;

    @GET
    public List<Fruit> list() {
        return fruitService.list();
    }

    @POST
    public List<Fruit> add(Fruit fruit) {
        fruitService.add(fruit);
        return list();
    }
}

实现非常简单,你只需要使用 Jakarta REST 注释定义端点,并使用 `FruitService`列出/添加新水果。

The implementation is pretty straightforward, and you just need to define your endpoints using the Jakarta REST annotations and use the FruitService to list/add new fruits.

Configuring the MongoDB database

要配置的主要属性是访问 MongoDB 的 URL。几乎所有配置都可以包含在连接 URI 中,因此我们建议你这样做。你可以在 MongoDB 文档中找到更多信息:[role="bare"][role="bare"]https://docs.mongodb.com/manual/reference/connection-string/

The main property to configure is the URL to access to MongoDB. Almost all configuration can be included in the connection URI, so we advise you to do so. You can find more information in the MongoDB documentation: [role="bare"]https://docs.mongodb.com/manual/reference/connection-string/

示例配置如下所示:

A sample configuration should look like this:

# configure the mongoDB client for a replica set of two nodes
quarkus.mongodb.connection-string = mongodb://mongo1:27017,mongo2:27017

在此示例中,我们正在使用运行在 localhost: 上的单个实例:

In this example, we are using a single instance running on localhost:

# configure the mongoDB client for a single instance on localhost
quarkus.mongodb.connection-string = mongodb://localhost:27017

如果你需要更多配置属性,本指南末尾有一个完整列表。

If you need more configuration properties, there is a full list at the end of this guide.

默认情况下,Quarkus 会限制在应用程序内使用 JNDI,作为预防措施,尝试减轻与 Log4Shell 类似的任何将来漏洞。由于 `mongo+srv`协议通常用于连接到 MongoDB 需要 JNDI,因此在使用 MongoDB 客户端扩展时,此保护会自动禁用。

By default, Quarkus will restrict the use of JNDI within an application, as a precaution to try and mitigate any future vulnerabilities similar to Log4Shell. Because the mongo+srv protocol often used to connect to MongoDB requires JNDI, this protection is automatically disabled when using the MongoDB client extension.

Use the MongoDB Dev Services

请参阅 MongoDB Dev Services

Multiple MongoDB Clients

MongoDB 允许你配置多个客户端。使用多个客户端与拥有单个客户端的工作方式相同。

MongoDB allows you to configure multiple clients. Using several clients works the same way as having a single client.

quarkus.mongodb.connection-string = mongodb://login:pass@mongo1:27017/database

quarkus.mongodb.users.connection-string = mongodb://mongo2:27017/userdb
quarkus.mongodb.inventory.connection-string = mongodb://mongo3:27017/invdb,mongo4:27017/invdb

请注意,密钥中有多余的部分(users`和 `inventory`段)。语法如下:`quarkus.mongodb.[optional name.][mongo connection property]。如果省略名称,则配置默认客户端。

Notice there’s an extra bit in the key (the users and inventory segments). The syntax is as follows: quarkus.mongodb.[optional name.][mongo connection property]. If the name is omitted, it configures the default client.

使用多个 MongoDB 客户端通过允许连接到多个 MongoDB 集群为 MongoDB 启用多租户。如果你想连接到同一集群中的多个数据库,多个客户端是 *not*必要的,因为单个客户端能够访问同一集群中的所有数据库(就像 JDBC 连接能够访问同一数据库中的多个架构一样)。

The use of multiple MongoDB clients enables multi-tenancy for MongoDB by allowing to connect to multiple MongoDB clusters. If you want to connect to multiple databases inside the same cluster, multiple clients are not necessary as a single client is able to access all databases in the same cluster (as a JDBC connection is able to access to multiple schemas inside the same database).

Named Mongo client Injection

在使用多个客户端时,每个 MongoClient,你可以使用 `io.quarkus.mongodb.MongoClientName`限定符选择要注入的客户端。使用上述属性配置三个不同的客户端,你还可以按如下方式注入每个客户端:

When using multiple clients, each MongoClient, you can select the client to inject using the io.quarkus.mongodb.MongoClientName qualifier. Using the above properties to configure three different clients, you can also inject each one as follows:

@Inject
MongoClient defaultMongoClient;

@Inject
@MongoClientName("users")
MongoClient mongoClient1;

@Inject
@MongoClientName("inventory")
ReactiveMongoClient mongoClient2;

Running a MongoDB Database

由于默认情况下,`MongoClient`配置为访问端口为 27017(默认 MongoDB 端口)上的本地 MongoDB 数据库,如果你在此端口上运行本地数据库,在能够测试它之前不必再做任何操作!

As by default, MongoClient is configured to access a local MongoDB database on port 27017 (the default MongoDB port), if you have a local running database on this port, there is nothing more to do before being able to test it!

如果你想使用 Docker 运行 MongoDB 数据库,可以使用以下命令启动一个:

If you want to use Docker to run a MongoDB database, you can use the following command to launch one:

docker run -ti --rm -p 27017:27017 mongo:4.4

如果您使用 Dev Services,手动启动容器不是必需的。

If you use dev-services, launching the container manually is not necessary.

Creating a frontend

现在,让我们添加一个简单的网页与我们的 `FruitResource`Quarkus 交互。Quarkus 会自动提供位置为 `META-INF/resources`目录之下的静态资源。在 `src/main/resources/META-INF/resources`目录中,用其中内容添加一个 `fruits.html`文件,来自该 fruits.html文件。

Now let’s add a simple web page to interact with our FruitResource. Quarkus automatically serves static resources located under the META-INF/resources directory. In the src/main/resources/META-INF/resources directory, add a fruits.html file with the content from this fruits.html file in it.

你现在可以与你的 REST 服务进行交互:

You can now interact with your REST service:

Reactive MongoDB Client

Quarkus 中包含一个响应式 MongoDB 客户端。使用它与使用传统 MongoDB 客户端一样容易。您可以重写之前的示例以使用它,比如以下示例。

A reactive MongoDB Client is included in Quarkus. Using it is as easy as using the classic MongoDB Client. You can rewrite the previous example to use it like the following.

Mutiny

MongoDB 响应式客户端使用 Mutiny 响应式类型。如果您不熟悉 Mutiny,请查看 Mutiny - an intuitive reactive programming library

The MongoDB reactive client uses Mutiny reactive types. If you are not familiar with Mutiny, check Mutiny - an intuitive reactive programming library.

package org.acme.mongodb;

import io.quarkus.mongodb.reactive.ReactiveMongoClient;
import io.quarkus.mongodb.reactive.ReactiveMongoCollection;
import io.smallrye.mutiny.Uni;
import org.bson.Document;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;

@ApplicationScoped
public class ReactiveFruitService {

    @Inject
    ReactiveMongoClient mongoClient;

    public Uni<List<Fruit>> list() {
        return getCollection().find()
                .map(doc -> {
                    Fruit fruit = new Fruit();
                    fruit.setName(doc.getString("name"));
                    fruit.setDescription(doc.getString("description"));
                    return fruit;
                }).collect().asList();
    }

    public Uni<Void> add(Fruit fruit) {
        Document document = new Document()
                .append("name", fruit.getName())
                .append("description", fruit.getDescription());
        return getCollection().insertOne(document)
                .onItem().ignore().andContinueWithNull();
    }

    private ReactiveMongoCollection<Document> getCollection() {
        return mongoClient.getDatabase("fruit").getCollection("fruit");
    }
}
package org.acme.mongodb;

import io.smallrye.mutiny.Uni;

import java.util.List;

import jakarta.inject.Inject;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.core.MediaType;

@Path("/reactive_fruits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReactiveFruitResource {

    @Inject
    ReactiveFruitService fruitService;

    @GET
    public Uni<List<Fruit>> list() {
        return fruitService.list();
    }

    @POST
    public Uni<List<Fruit>> add(Fruit fruit) {
        return fruitService.add(fruit)
                .onItem().ignore().andSwitchTo(this::list);
    }
}

Simplifying MongoDB Client usage using BSON codec

通过使用 Bson Codec,MongoDB 客户端将自动处理您的领域对象与 MongoDB `Document`之间的转换。

By using a Bson Codec, the MongoDB Client will take care of the transformation of your domain object to/from a MongoDB Document automatically.

首先,您需要创建一个 Bson Codec,它将告诉 Bson 如何从您的实体到 MongoDB Document`转换以及如何进行反向转换。我们在这里使用 `CollectibleCodec,因为我们的对象可以从数据库进行检索(它具有 MongoDB 标识符),如果不是这样,我们使用 `Codec`即可。有关编解码器文档的更多信息:[role="bare"]https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/data-formats/codecs/。

First you need to create a Bson Codec that will tell Bson how to transform your entity to/from a MongoDB Document. Here we use a CollectibleCodec as our object is retrievable from the database (it has a MongoDB identifier), if not we would have used a Codec instead. More information in the codec documentation: [role="bare"]https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/data-formats/codecs/.

package org.acme.mongodb.codec;

import com.mongodb.MongoClientSettings;
import org.acme.mongodb.Fruit;
import org.bson.Document;
import org.bson.BsonWriter;
import org.bson.BsonValue;
import org.bson.BsonReader;
import org.bson.BsonString;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;

import java.util.UUID;

public class FruitCodec implements CollectibleCodec<Fruit> {

    private final Codec<Document> documentCodec;

    public FruitCodec() {
        this.documentCodec = MongoClientSettings.getDefaultCodecRegistry().get(Document.class);
    }

    @Override
    public void encode(BsonWriter writer, Fruit fruit, EncoderContext encoderContext) {
        Document doc = new Document();
        doc.put("name", fruit.getName());
        doc.put("description", fruit.getDescription());
        documentCodec.encode(writer, doc, encoderContext);
    }

    @Override
    public Class<Fruit> getEncoderClass() {
        return Fruit.class;
    }

    @Override
    public Fruit generateIdIfAbsentFromDocument(Fruit document) {
        if (!documentHasId(document)) {
            document.setId(UUID.randomUUID().toString());
        }
        return document;
    }

    @Override
    public boolean documentHasId(Fruit document) {
        return document.getId() != null;
    }

    @Override
    public BsonValue getDocumentId(Fruit document) {
        return new BsonString(document.getId());
    }

    @Override
    public Fruit decode(BsonReader reader, DecoderContext decoderContext) {
        Document document = documentCodec.decode(reader, decoderContext);
        Fruit fruit = new Fruit();
        if (document.getString("id") != null) {
            fruit.setId(document.getString("id"));
        }
        fruit.setName(document.getString("name"));
        fruit.setDescription(document.getString("description"));
        return fruit;
    }
}

然后,您需要创建一个 `CodecProvider`将此 `Codec`链接到 `Fruit`类。

Then you need to create a CodecProvider to link this Codec to the Fruit class.

package org.acme.mongodb.codec;

import org.acme.mongodb.Fruit;
import org.bson.codecs.Codec;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistry;

public class FruitCodecProvider implements CodecProvider {
    @Override
    public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
        if (clazz.equals(Fruit.class)) {
            return (Codec<T>) new FruitCodec();
        }
        return null;
    }

}

Quarkus 会处理以 @Singleton`范围的 CDI bean 的形式为您注册 `CodecProvider

Quarkus takes care of registering the CodecProvider for you as a CDI bean of @Singleton scope.

最后,当从数据库获取 `MongoCollection`时,您可以直接使用 `Fruit`类而不是 `Document`类,编解码器会将 `Document`类与 `Fruit`类自动进行映射。

Finally, when getting the MongoCollection from the database you can use directly the Fruit class instead of the Document one, the codec will automatically map the Document to/from your Fruit class.

以下是使用 `FruitCodec`的 `MongoCollection`的示例。

Here is an example of using a MongoCollection with the FruitCodec.

package org.acme.mongodb;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@ApplicationScoped
public class CodecFruitService {

    @Inject MongoClient mongoClient;

    public List<Fruit> list(){
        List<Fruit> list = new ArrayList<>();
        MongoCursor<Fruit> cursor = getCollection().find().iterator();

        try {
            while (cursor.hasNext()) {
                list.add(cursor.next());
            }
        } finally {
            cursor.close();
        }
        return list;
    }

    public void add(Fruit fruit){
        getCollection().insertOne(fruit);
    }

    private MongoCollection<Fruit> getCollection(){
        return mongoClient.getDatabase("fruit").getCollection("fruit", Fruit.class);
    }
}

The POJO Codec

POJO Codec 提供一组注释,启用对 POJO 映射到 MongoDB 集合的方式进行自定义,而这个编解码器由 Quarkus 自动初始化

The POJO Codec provides a set of annotations that enable the customization of the way a POJO is mapped to a MongoDB collection and this codec is initialized automatically by Quarkus

其中一个注释是 @BsonDiscriminator 注释,它允许通过在文档中添加鉴别器字段,以在单个 MongoDB 集合中存储多个 Java 类型。当处理抽象类型或接口时,它会很有用。

One of these annotations is the @BsonDiscriminator annotation that allows to storage multiple Java types in a single MongoDB collection by adding a discriminator field inside the document. It can be useful when working with abstract types or interfaces.

Quarkus 会自动使用 POJO 编解码器注册所有使用 @BsonDiscriminator 注释的类。

Quarkus will automatically register all the classes annotated with @BsonDiscriminator with the POJO codec.

POJO 编解码器通过 PropertyCodecProvider 增强了泛型支持,Quarkus 会使用 POJO 编解码器自动注册任何 PropertyCodecProvider(这些类自动做成 @Singleton 范围的 CDI bean)。在构建本机可执行文件以及使用泛型类型时,您可能需要为反射注册类型参数。

The POJO Codec have enhanced generic support via PropertyCodecProvider, Quarkus will automatically register any PropertyCodecProvider with the POJO Codec (these classes are automatically made CDI beans of @Singleton scope). When building native executables and using generic types, you might need to register the type arguments for reflection.

Simplifying MongoDB with Panache

MongoDB with Panache 扩展通过提供类似于 Hibernate ORM with Panache中的活动记录实体(和存储库)促进了 MongoDB 的使用,并且专注于让您的实体编写起来轻松有趣。

The MongoDB with Panache extension facilitates the usage of MongoDB by providing active record style entities (and repositories) like you have in Hibernate ORM with Panache and focuses on making your entities trivial and fun to write in Quarkus.

Schema migration with Liquibase

Liquibase MongoDB 扩展促进了 MongoDB 数据库的初始化,包括索引和初始数据。它实现了与 Liquibase 为 SQL 数据库提供的相同模式迁移工具。

The Liquibase MongoDB extension facilitates the initialization of a MongoDB database including indices and initial data. It implements the same schema migration facilities that Liquibase offers for SQL databases.

Connection Health Check

如果您使用 quarkus-smallrye-health 扩展,quarkus-mongodb-client 会自动添加一个准备就绪健康检查以验证与群集的连接。

If you are using the quarkus-smallrye-health extension, quarkus-mongodb-client will automatically add a readiness health check to validate the connection to the cluster.

因此,当您访问应用程序的 /q/health/ready 端点时,您将获得有关连接验证状态的信息。

So when you access the /q/health/ready endpoint of your application you will have information about the connection validation status.

可以通过在 application.properties 中将 quarkus.mongodb.health.enabled 属性设置为 false 来禁用此行为。

This behavior can be disabled by setting the quarkus.mongodb.health.enabled property to false in your application.properties.

Metrics

如果您正在使用 quarkus-micrometerquarkus-smallrye-metrics 扩展,quarkus-mongodb-client 可以提供有关连接池的指标。必须先通过在 application.properties 中将 quarkus.mongodb.metrics.enabled 属性设置为 true 来启用此行为。

If you are using the quarkus-micrometer or quarkus-smallrye-metrics extension, quarkus-mongodb-client can provide metrics about the connection pools. This behavior must first be enabled by setting the quarkus.mongodb.metrics.enabled property to true in your application.properties.

因此,当您访问应用程序的 /q/metrics 端点时,您将获得有关连接池状态的信息。在使用 SmallRye Metrics 时,可以 vendor 在范围内找到连接池指标。

So when you access the /q/metrics endpoint of your application you will have information about the connection pool status. When using SmallRye Metrics, connection pool metrics will be available under the vendor scope.

Tracing

要对 MongoDB 使用追踪,需要将 quarkus-opentelemetry 扩展添加到你的项目中。

To use tracing with MongoDB, you need to add the quarkus-opentelemetry extension to your project.

即使所有追踪基础设施都已到位,MongoDB 追踪也不会默认启用,你需要通过设置以下属性来启用:

Even with all the tracing infrastructure in place the mongodb tracing is not enabled by default, and you need to enable it by setting this property:

# enable tracing
quarkus.mongodb.tracing.enabled=true

Testing helpers

Dev Services for MongoDB 是为单元测试启动 MongoDB 数据库的最佳选择。

dev-services is your best option to start a MongoDB database for your unit tests.

但如果你无法使用它,则可以使用 Quarkus 提供的两个 QuarkusTestResourceLifecycleManager 之一启动 MongoDB 数据库。它们依赖 Flapdoodle embedded MongoDB

But if you can’t use it, you can start a MongoDB database using one of the two QuarkusTestResourceLifecycleManager that Quarkus provides. They rely on Flapdoodle embedded MongoDB.

  • io.quarkus.test.mongodb.MongoTestResource will start a single instance on port 27017.

  • io.quarkus.test.mongodb.MongoReplicaSetTestResource will start a replicaset with two instances, one on port 27017 and the other on port 27018.

要使用它们,你需要将 io.quarkus:quarkus-test-mongodb 依赖项添加到你的 pom.xml。

To use them, you need to add the io.quarkus:quarkus-test-mongodb dependency to your pom.xml.

要了解更多关于使用 QuarkusTestResourceLifecycleManager 的信息,请参阅 Quarkus test resource

For more information about the usage of a QuarkusTestResourceLifecycleManager please read Quarkus test resource.

要设置在启动时 MongoDB 将侦听的目标端口,应使用以下代码:

To set the desired port MongoDB will listen to when it is launched, the following code should be used:

@WithTestResource(value = MongoTestResource.class, initArgs = @ResourceArg(name = MongoTestResource.PORT, value = "27017"))

要设置将启动的目标 MongoDB 版本,应使用以下代码:

To set the desired MongoDB version that will be launched, the following code should be used:

@WithTestResource(value = MongoTestResource.class, initArgs = @ResourceArg(name = MongoTestResource.VERSION, value = "V5_0"))

所使用的字符串值可以是 de.flapdoodle.embed.mongo.distribution.Versionde.flapdoodle.embed.mongo.distribution.Version.Main 枚举中的任意一个。如果没有指定版本,则默认使用 Version.Main.V4_0

The string value used can be any of one of the de.flapdoodle.embed.mongo.distribution.Version or de.flapdoodle.embed.mongo.distribution.Version.Main enums. If no version is specified, Version.Main.V4_0 is used by default.

The legacy client

我们默认不包含传统 MongoDB 客户端。它包含现已停用的 MongoDB Java API(DB、DBCollection、……),而 com.mongodb.MongoClient 现在已被 com.mongodb.client.MongoClient 取代。

We don’t include the legacy MongoDB client by default. It contains the now retired MongoDB Java API (DB, DBCollection,…​ ) and the com.mongodb.MongoClient that is now superseded by com.mongodb.client.MongoClient.

如果你希望使用传统 API,则需要将以下依赖添加到你的构建文件中:

If you want to use the legacy API, you need to add the following dependency to your build file:

pom.xml
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-legacy</artifactId>
</dependency>
build.gradle
implementation("org.mongodb:mongodb-driver-legacy")

Building a native executable

你可以在原生可执行文件中使用 MongoDB 客户端。

You can use the MongoDB client in a native executable.

如果您想使用 SSL/TLS 加密,则需要在 `application.properties`中添加这些属性:

If you want to use SSL/TLS encryption, you need to add these properties in your application.properties:

quarkus.mongodb.tls=true
quarkus.mongodb.tls-insecure=true # only if TLS certificate cannot be validated

然后可以使用以下命令构建可执行的原始文件:

You can then build a native executable with the usual command:

Unresolved directive in mongodb.adoc - include::{includes}/devtools/build-native.adoc[]

运行起来跟执行 `./target/mongodb-quickstart-1.0.0-SNAPSHOT-runner`一样简单。

Running it is as simple as executing ./target/mongodb-quickstart-1.0.0-SNAPSHOT-runner.

然后您可以将浏览器指向 http://localhost:8080/fruits.html,并使用您的应用程序。

You can then point your browser to http://localhost:8080/fruits.html and use your application.

目前,Quarkus 不支持在原生模式下的 Client-Side Field Level Encryption

Currently, Quarkus doesn’t support Client-Side Field Level Encryption in native mode.

如果您在以原生模式运行应用程序时遇到以下错误:Failed to encode 'MyObject'. Encoding 'myVariable' errored with: Can’t find a codec for class org.acme.MyVariable.,则表示 GraalVM 不知道 `org.acme.MyVariable`类,解决办法是向 `MyVariable`类添加 `@RegisterForReflection`注解。可以在 native application tips页面上找到关于 `@RegisterForReflection`注解的更多详情。

If you encounter the following error when running your application in native mode: Failed to encode 'MyObject'. Encoding 'myVariable' errored with: Can’t find a codec for class org.acme.MyVariable. This means that the org.acme.MyVariable class is not known to GraalVM, the remedy is to add the @RegisterForReflection annotation to your MyVariable class. More details about the @RegisterForReflection annotation can be found on the native application tips page.

Using mongo+srv:// urls

`mongo+srv://`URL 在 JVM 模式下开箱即用。但是,在原生模式中,由 MongoDB 客户端提供的默认 DNS 解析器使用 JNDI,并且在原生模式下不起作用。

mongo+srv:// urls are supported out of the box in JVM mode. However, in native, the default DNS resolver, provided by the MongoDB client, uses JNDI and does not work in native mode.

如果您需要在原生模式下使用 mongo+srv://,则可以配置一个备用 DNS 解析器。此功能正在 experimental,它可能会导致 JVM 应用程序和原生应用程序之间存在差异。

If you need to use mongo+srv:// in native mode, you can configure an alternative DNS resolver. This feature is experimental and may introduce a difference between JVM applications and native applications.

若要启用替代 DNS 解析器,请使用:

To enable the alternative DNS resolver, use:

quarkus.mongodb.native.dns.use-vertx-dns-resolver=true

如属性名称所示,它使用 Vert.x 检索 DNS 记录。默认情况下,它会尝试从 /etc/resolv.conf`读取第一个 `nameserver,如果此文件存在的话。您也可以配置您的 DNS 服务器:

As indicated in the property name, it uses Vert.x to retrieve the DNS records. By default, it tries to read the first nameserver from /etc/resolv.conf, if this file exists. You can also configure your DNS server:

quarkus.mongodb.native.dns.use-vertx-dns-resolver=true
quarkus.mongodb.native.dns.server-host=10.0.0.1
quarkus.mongodb.native.dns.server-port=53 # 53 is the default port

此外,您还可以使用以下方法配置查找超时:

Also, you can configure the lookup timeout using:

quarkus.mongodb.native.dns.use-vertx-dns-resolver=true
quarkus.mongodb.native.dns.lookup-timeout=10s # the default is 5s

Customize the Mongo client configuration programmatically

如果您需要以编程方式定制 Mongo 客户端配置,则需要实现 `io.quarkus.mongodb.runtime.MongoClientCustomizer`接口并将其公开为 CDI _application scoped_bean:

If you need to customize the Mongo client configuration programmatically, you need to implement the io.quarkus.mongodb.runtime.MongoClientCustomizer interface and expose it as a CDI application scoped bean:

@ApplicationScoped
public class MyCustomizer implements MongoClientCustomizer {

    @Override
    public MongoClientSettings.Builder customize(MongoClientSettings.Builder builder) {
        return builder.applicationName("my-app");
    }
}

该 bean 可以使用 `@MongoClientName`限定符来自定义特定客户端,以指示客户端名称。当没有限定符时,它会自定义默认客户端。每个客户端至多可以使用一个定制器。如果检测到针对同一客户端的多个定制器,则会在构建时引发异常。

The bean can customize a specific client using the @MongoClientName qualifier to indicate the client name. When there is no qualifier, it customizes the default client. At most one customizer can be used per client. If multiple customizers targeting the same client are detected, an exception is thrown at build time.

此功能可用于配置客户端侧字段级加密 (CSFLE)。请按照 the Mongo web site中的说明配置 CSFLE:

This feature can be used to configure client-side field level encryption (CSFLE). Follows the instructions from the Mongo web site to configure CSFLE:

@ApplicationScoped
public class MyCustomizer implements MongoClientCustomizer {
    @Override
    public MongoClientSettings.Builder customize(MongoClientSettings.Builder builder) {
        Map<String, Map<String, Object>> kmsProviders = getKmsProviders();
        String dek = getDataEncryptionKey();
        Map<String, BsonDocument> schema = getSchema(dek);

        Map<String, Object> extraOptions = new HashMap<>();
        extraOptions.put("cryptSharedLibPath", "<path to crypt shared library>");

        return builder.autoEncryptionSettings(AutoEncryptionSettings.builder()
                .keyVaultNamespace(KEY_VAULT_NAMESPACE)
                .kmsProviders(kmsProviders)
                .schemaMap(schemaMap)
                .extraOptions(extraOptions)
                .build());
    }
}

一般而言,客户端侧字段级加密和依赖于 Mongo Crypt的功能都不支持原生模式。

Client-side field level encryption, and feature relying on Mongo Crypt in general, are not supported in native mode.

Configuration Reference

Unresolved directive in mongodb.adoc - include::{generated-dir}/config/quarkus-mongodb-client.adoc[]