Reactive SQL Clients

Reactive SQL 客户端具有直接的 API,重点是可扩展性和低开销。目前支持以下数据库服务器:

  • IBM Db2

  • PostgreSQL

  • MariaDB/MySQL

  • Microsoft SQL Server

  • Oracle

Oracle 的 Reactive SQL 客户端被认为是 tech preview。 在 tech preview 模式下,需要及早反馈来完善该理念。在解决方案成熟之前,平台的稳定性无法得到保证。欢迎在我们的 mailing list 或我们的 GitHub issue tracker 中作为问题提供反馈。

在本指南中,你将学习如何实现一个简单的 CRUD 应用程序,通过 RESTful API 公开存储在 PostgreSQL 中的数据。

可以在本文档的底部找到每个客户端的扩展名和连接池类名。

如果你不熟悉 Quarkus Vert.x 扩展,请考虑先阅读 Using Eclipse Vert.x 指南。

该应用程序应管理水果实体:

src/main/java/org/acme/reactive/crud/Fruit.java
package org.acme.reactive.crud;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.pgclient.PgPool;
import io.vertx.mutiny.sqlclient.Row;
import io.vertx.mutiny.sqlclient.RowSet;
import io.vertx.mutiny.sqlclient.Tuple;

public class Fruit {

    public Long id;

    public String name;

    public Fruit() {
        // default constructor.
    }

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

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

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

如果你在开发模式下启动应用程序,Quarkus 会开箱即用地为你提供一个 zero-config database。 你还可以预先启动一个数据库:

docker run -it --rm=true --name quarkus_test -e POSTGRES_USER=quarkus_test -e POSTGRES_PASSWORD=quarkus_test -e POSTGRES_DB=quarkus_test -p 5432:5432 postgres:14.1

Solution

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

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

此解决方案位于 getting-started-reactive-crud directory

Installing

Reactive PostgreSQL Client extension

首先,确保你的项目已启用 quarkus-reactive-pg-client 扩展。如果你正在创建一个新项目,请使用以下命令:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

如果你已经创建了一个项目,可以使用 add-extension 命令将 reactive-pg-client 扩展添加到现有的 Quarkus 项目:

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

否则,你可以手动将依赖项添加到你的构建文件:

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

Mutiny

Quarkus REST(以前称为 RESTEasy Reactive)开箱即用地支持 Mutiny 类型(例如 UniMulti)。

在本指南中,我们将使用 Reactive PostgreSQL 客户端的 Mutiny API。如果你不熟悉 Mutiny,请查看 Mutiny - an intuitive reactive programming library

JSON Binding

我们将在 JSON 格式下通过 HTTP 公开 Fruit 实例。因此,你必须还添加 quarkus-rest-jackson 扩展:

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

如果你不想使用命令行,请手动将依赖项添加到你的构建文件:

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

当然,这仅仅是本指南的要求,而不是使用 Reactive PostgreSQL 客户端的任何应用程序的要求。

Configuring

Reactive PostgreSQL 客户端可以通过标准的 Quarkus 数据源属性和反应式 URL 进行配置:

src/main/resources/application.properties
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=quarkus_test
quarkus.datasource.reactive.url=postgresql://localhost:5432/quarkus_test

使用该方法,你可以创建 FruitResource 框架并注入一个 io.vertx.mutiny.pgclient.PgPool 实例:

src/main/java/org/acme/vertx/FruitResource.java
package org.acme.reactive.crud;

import java.net.URI;

import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.Response.Status;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.pgclient.PgPool;

@Path("fruits")
public class FruitResource {

    private final PgPool client;

    public FruitResource(PgPool client) {
        this.client = client;
    }
}

Database schema and seed data

在实现 REST 端点和数据管理代码之前,我们必须设置数据库模式。在前期插入一些数据也比较方便。

对于生产,我们建议使用类似于 Flyway database migration tool 的内容。但是对于开发,我们可以在启动时简单地删除并创建表,然后插入一些水果。

/src/main/java/org/acme/reactive/crud/DBInit.java
package org.acme.reactive.crud;

import io.quarkus.runtime.StartupEvent;
import io.vertx.mutiny.pgclient.PgPool;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

@ApplicationScoped
public class DBInit {

    private final PgPool client;
    private final boolean schemaCreate;

    public DBInit(PgPool client, @ConfigProperty(name = "myapp.schema.create", defaultValue = "true") boolean schemaCreate) {
        this.client = client;
        this.schemaCreate = schemaCreate;
    }

    void onStart(@Observes StartupEvent ev) {
        if (schemaCreate) {
            initdb();
        }
    }

    private void initdb() {
        // TODO
    }
}

您可以在 application.properties 文件中覆盖 myapp.schema.create 属性的默认值。

快准备好了!在开发模式下初始化数据库,我们将使用客户端简单的 query 方法。它返回一个 Uni,因此可以组合来顺序执行查询:

client.query("DROP TABLE IF EXISTS fruits").execute()
    .flatMap(r -> client.query("CREATE TABLE fruits (id SERIAL PRIMARY KEY, name TEXT NOT NULL)").execute())
    .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Kiwi')").execute())
    .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Durian')").execute())
    .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Pomelo')").execute())
    .flatMap(r -> client.query("INSERT INTO fruits (name) VALUES ('Lychee')").execute())
    .await().indefinitely();

想知道为什么我们必须在最新查询完成之前进行阻塞吗?此代码是方法的一部分,它 @Observes StartupEvent,Quarkus 以同步方式调用它。因此,过早返回会导致在数据库尚未准备就绪时处理请求。

就是这样!到目前为止,我们已经了解了如何配置池化客户端和执行简单查询。我们现在可以开发数据管理代码并实现我们的 RESTful 端点。

Using

Query results traversal

在开发模式下,数据库在 fruits 表中设置了几行。若要检索所有数据,我们将再次使用 query 方法:

/src/main/java/org/acme/reactive/crud/Fruit.java
    public static Multi<Fruit> findAll(PgPool client) {
        return client.query("SELECT id, name FROM fruits ORDER BY name ASC").execute()
                .onItem().transformToMulti(set -> Multi.createFrom().iterable(set)) (1)
                .onItem().transform(Fruit::from); (2)
    }

    private static Fruit from(Row row) {
        return new Fruit(row.getLong("id"), row.getString("name"));
    }
1 io.vertx.mutiny.sqlclient.RowSet 变成 Multi&lt;Row&gt;
2 将每个 io.vertx.mutiny.sqlclient.Row 转换为 Fruit

Fruit#from 方法将 Row 实例转换为 Fruit 实例。将其提取出来是为了方便实现其他数据管理方法。

然后,添加获取后端所有水果的端点:

src/main/java/org/acme/vertx/FruitResource.java
@GET
public Multi<Fruit> get() {
    return Fruit.findAll(client);
}

现在,使用以下方法在开发模式下启动 Quarkus:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

最后,打开浏览器并导航到 [role="bare"][role="bare"]http://localhost:8080/fruits,您应该看到:

[{"id":2,"name":"Durian"},{"id":1,"name":"Kiwi"},{"id":4,"name":"Lychee"},{"id":3,"name":"Pomelo"}]

Prepared queries

Reactive PostgreSQL 客户端还可以准备查询并获取在执行时替换 SQL 语句中的参数:

client.preparedQuery("SELECT id, name FROM fruits WHERE id = $1").execute(Tuple.of(id))

对于 PostgreSQL,SQL 字符串可以通过使用 $1$2 、 …​等位置来引用参数。有关其他数据库,请参考 Database Clients details 部分。

与简单的 query 方法类似, preparedQuery 返回 PreparedQuery<RowSet<Row>> 的实例。借助此工具,我们能够安全地使用用户提供的 id 来获取特定水果的详细信息:

src/main/java/org/acme/vertx/Fruit.java
public static Uni<Fruit> findById(PgPool client, Long id) {
    return client.preparedQuery("SELECT id, name FROM fruits WHERE id = $1").execute(Tuple.of(id)) (1)
            .onItem().transform(RowSet::iterator) (2)
            .onItem().transform(iterator -> iterator.hasNext() ? from(iterator.next()) : null); (3)
}
1 创建一个 Tuple 来保存已准备的查询参数。
2 获取 RowSet 结果的 Iterator
3 如果找到实体,从 Row 创建 Fruit 实例。

在 Jakarta REST 资源中:

src/main/java/org/acme/vertx/FruitResource.java
@GET
@Path("{id}")
public Uni<Response> getSingle(Long id) {
    return Fruit.findById(client, id)
            .onItem().transform(fruit -> fruit != null ? Response.ok(fruit) : Response.status(Status.NOT_FOUND)) (1)
            .onItem().transform(ResponseBuilder::build); (2)
}
1 准备一个 Jakarta REST 响应,其中包含找到的 Fruit 实例或 404 状态代码。
2 构建并发送响应。

同样的逻辑适用于保存 Fruit 时:

src/main/java/org/acme/vertx/Fruit.java
public Uni<Long> save(PgPool client) {
    return client.preparedQuery("INSERT INTO fruits (name) VALUES ($1) RETURNING id").execute(Tuple.of(name))
            .onItem().transform(pgRowSet -> pgRowSet.iterator().next().getLong("id"));
}

在 Web 资源中,我们处理 POST 请求:

src/main/java/org/acme/vertx/FruitResource.java
@POST
public Uni<Response> create(Fruit fruit) {
    return fruit.save(client)
            .onItem().transform(id -> URI.create("/fruits/" + id))
            .onItem().transform(uri -> Response.created(uri).build());
}

Result metadata

RowSet 不仅持有内存中的数据,还提供一些关于数据本身的信息,例如:

  • 查询影响的行数(根据查询类型插入/删除/更新/检索),

  • the column names.

让我们用它来支持从数据库中删除水果:

src/main/java/org/acme/vertx/Fruit.java
public static Uni<Boolean> delete(PgPool client, Long id) {
    return client.preparedQuery("DELETE FROM fruits WHERE id = $1").execute(Tuple.of(id))
            .onItem().transform(pgRowSet -> pgRowSet.rowCount() == 1); (1)
}
1 检查元数据以确定水果是否已实际删除。

并在 Web 资源中处理 HTTP DELETE 方法:

src/main/java/org/acme/vertx/FruitResource.java
@DELETE
@Path("{id}")
public Uni<Response> delete(Long id) {
    return Fruit.delete(client, id)
            .onItem().transform(deleted -> deleted ? Status.NO_CONTENT : Status.NOT_FOUND)
            .onItem().transform(status -> Response.status(status).build());
}

通过实现 GETPOSTDELETE 方法,我们现在可以创建一个最小的网页来试用 RESTful 应用程序。我们使用 jQuery 来简化与后端的交互:

/src/main/resources/META-INF/resources/fruits.html
<!doctype html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Reactive REST - Quarkus</title>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"
            integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
    <script type="application/javascript" src="fruits.js"></script>
</head>
<body>

<h1>Fruits API Testing</h1>

<h2>All fruits</h2>
<div id="all-fruits"></div>

<h2>Create Fruit</h2>
<input id="fruit-name" type="text">
<button id="create-fruit-button" type="button">Create</button>
<div id="create-fruit"></div>

</body>
</html>

Quarkus 自动供应位于 META-INF/resources 目录下的静态资源。

在 JavaScript 代码中,我们需要一个函数来刷新水果列表,当:

  • 页面加载时,或

  • 添加了一个水果,或

  • a fruit is deleted.

/src/main/resources/META-INF/resources/fruits.js
function refresh() {
    $.get('/fruits', function (fruits) {
        var list = '';
        (fruits || []).forEach(function (fruit) { (1)
            list = list
                + '<tr>'
                + '<td>' + fruit.id + '</td>'
                + '<td>' + fruit.name + '</td>'
                + '<td><a href="#" onclick="deleteFruit(' + fruit.id + ')">Delete</a></td>'
                + '</tr>'
        });
        if (list.length > 0) {
            list = ''
                + '<table><thead><th>Id</th><th>Name</th><th></th></thead>'
                + list
                + '</table>';
        } else {
            list = "No fruits in database"
        }
        $('#all-fruits').html(list);
    });
}

function deleteFruit(id) {
    $.ajax('/fruits/' + id, {method: 'DELETE'}).then(refresh);
}

$(document).ready(function () {

    $('#create-fruit-button').click(function () {
        var fruitName = $('#fruit-name').val();
        $.post({
            url: '/fruits',
            contentType: 'application/json',
            data: JSON.stringify({name: fruitName})
        }).then(refresh);
    });

    refresh();
});
1 数据库为空时未定义 fruits 参数。

完成!导航至 [role="bare"][role="bare"]http://localhost:8080/fruits.html 并读取/创建/删除一些水果。

Database Clients details

Database Extension name Pool class name Placeholders

IBM Db2

quarkus-reactive-db2-client

io.vertx.mutiny.db2client.DB2Pool

?

MariaDB/MySQL

quarkus-reactive-mysql-client

io.vertx.mutiny.mysqlclient.MySQLPool

?

Microsoft SQL Server

quarkus-reactive-mssql-client

io.vertx.mutiny.mssqlclient.MSSQLPool

@p1, @p2, etc.

Oracle

quarkus-reactive-oracle-client

io.vertx.mutiny.oracleclient.OraclePool

?

PostgreSQL

quarkus-reactive-pg-client

io.vertx.mutiny.pgclient.PgPool

$1, $2, etc.

Transactions

反应式 SQL 客户端支持事务。事务通过 io.vertx.mutiny.sqlclient.SqlConnection#begin 启动并通过 io.vertx.mutiny.sqlclient.Transaction#commitio.vertx.mutiny.sqlclient.Transaction#rollback 终止。所有这些操作都是异步的:

  • connection.begin() returns a Uni<Transaction>

  • transaction.commit()transaction.rollback() 返回 Uni&lt;Void&gt;

在响应式编程世界中管理事务可能很繁琐。您可以使用 io.vertx.mutiny.sqlclient.Pool#withTransaction 帮助程序方法,而不是编写重复且复杂的(因而容易出错!)代码。

以下代码段展示了如何在同一事务中运行 2 个插入:

public static Uni<Void> insertTwoFruits(PgPool client, Fruit fruit1, Fruit fruit2) {
    return client.withTransaction(conn -> {
        Uni<RowSet<Row>> insertOne = conn.preparedQuery("INSERT INTO fruits (name) VALUES ($1) RETURNING id")
                .execute(Tuple.of(fruit1.name));
        Uni<RowSet<Row>> insertTwo = conn.preparedQuery("INSERT INTO fruits (name) VALUES ($1) RETURNING id")
                .execute(Tuple.of(fruit2.name));

        return Uni.combine().all().unis(insertOne, insertTwo)
                // Ignore the results (the two ids)
                .discardItems();
    });
}

在此示例中,事务在成功时自动提交或在失败时回滚。

你还可以按照如下方式创建相关动作:

return client.withTransaction(conn -> conn

        .preparedQuery("INSERT INTO person (firstname,lastname) VALUES ($1,$2) RETURNING id")
        .execute(Tuple.of(person.getFirstName(), person.getLastName()))

        .onItem().transformToUni(id -> conn.preparedQuery("INSERT INTO addr (person_id,addrline1) VALUES ($1,$2)")
                .execute(Tuple.of(id.iterator().next().getLong("id"), person.getLastName())))

        .onItem().ignore().andContinueWithNull());

Working with batch query results

在执行批处理查询时,响应式 SQL 客户端会返回一个 RowSet,该批次的结果与第一个元素相对应。要获取以下批处理元素的结果,您必须调用 RowSet#next 方法,直到它返回 null

假设您想要更新一些行并计算受影响的行总数。您必须检查每个 RowSet

PreparedQuery<RowSet<Row>> preparedQuery = client.preparedQuery("UPDATE fruits SET name = $1 WHERE id = $2");

Uni<RowSet<Row>> rowSet = preparedQuery.executeBatch(Arrays.asList(
        Tuple.of("Orange", 1),
        Tuple.of("Pear", 2),
        Tuple.of("Apple", 3)));

Uni<Integer> totalAffected = rowSet.onItem().transform(res -> {
    int total = 0;
    do {
        total += res.rowCount(); (1)
    } while ((res = res.next()) != null); (2)
    return total;
});
1 计算 RowSet#rowCount 的和。
2 调用 RowSet#next,直到它返回 null

作为另一个示例,如果您想要加载您刚刚插入的所有行,您必须连接每个 RowSet 的内容:

PreparedQuery<RowSet<Row>> preparedQuery = client.preparedQuery("INSERT INTO fruits (name) VALUES ($1) RETURNING *");

Uni<RowSet<Row>> rowSet = preparedQuery.executeBatch(Arrays.asList(
        Tuple.of("Orange"),
        Tuple.of("Pear"),
        Tuple.of("Apple")));

// Generate a Multi of RowSet items
Multi<RowSet<Row>> rowSets = rowSet.onItem().transformToMulti(res -> {
    return Multi.createFrom().generator(() -> res, (rs, emitter) -> {
        RowSet<Row> next = null;
        if (rs != null) {
            emitter.emit(rs);
            next = rs.next();
        }
        if (next == null) {
            emitter.complete();
        }
        return next;
    });
});

// Transform each RowSet into Multi of Row items and Concatenate
Multi<Row> rows = rowSets.onItem().transformToMultiAndConcatenate(Multi.createFrom()::iterable);

Multiple Datasources

反应式 SQL 客户端支持定义多个数据源。

使用多个数据源的典型配置如下所示:

quarkus.datasource.db-kind=postgresql 1
quarkus.datasource.username=user-default
quarkus.datasource.password=password-default
quarkus.datasource.reactive.url=postgresql://localhost:5432/default

quarkus.datasource."additional1".db-kind=postgresql 2
quarkus.datasource."additional1".username=user-additional1
quarkus.datasource."additional1".password=password-additional1
quarkus.datasource."additional1".reactive.url=postgresql://localhost:5432/additional1

quarkus.datasource."additional2".db-kind=mysql 3
quarkus.datasource."additional2".username=user-additional2
quarkus.datasource."additional2".password=password-additional2
quarkus.datasource."additional2".reactive.url=mysql://localhost:3306/additional2
1 默认数据源 - 使用 PostgreSQL。
2 使用 PostgreSQL 的名为 additional1 的命名数据源。
3 使用 MySQL 的名为 additional2 的命名数据源。

然后你可以如下一般注入客户端:

@Inject 1
PgPool defaultClient;

@Inject
@ReactiveDataSource("additional1") 2
PgPool additional1Client;

@Inject
@ReactiveDataSource("additional2")
MySQLPool additional2Client;
1 注入默认数据源的客户端不需要任何特殊操作。
2 对于命名数据源,请将 @ReactiveDataSource CDI 限定符连同数据源名称作为其值使用。

UNIX Domain Socket connections

可以将 PostgreSQL 和 MariaDB/MySQL 客户端配置为通过 UNIX 域套接字连接服务器。

首先确保 native transport support 已启用。

然后配置数据库连接 URL。此步骤取决于数据库类型。

PostgreSQL

PostgreSQL 域套接字路径采用以下形式:<directory>/.s.PGSQL.<port>

数据库连接 URL 必须进行配置,以便:

  • host`是套接字路径中的 `directory

  • port`是套接字路径中的 `port

考虑以下套接字路径:/var/run/postgresql/.s.PGSQL.5432

在 `application.properties`中添加:

quarkus.datasource.reactive.url=postgresql://:5432/quarkus_test?host=/var/run/postgresql

MariaDB/MySQL

数据库连接 URL 必须进行配置,以便 `host`是套接字路径。

考虑以下套接字路径:/var/run/mysqld/mysqld.sock

在 `application.properties`中添加:

quarkus.datasource.reactive.url=mysql:///quarkus_test?host=/var/run/mysqld/mysqld.sock

Load-balancing connections

响应式 PostgreSQL 和 MariaDB/MySQL 客户端支持定义多种连接。

具有多种连接的典型配置如下:

quarkus.datasource.reactive.url=postgresql://host1:5432/default,postgresql://host2:5432/default,postgresql://host3:5432/default

这也可以用索引属性语法编写:

quarkus.datasource.reactive.url[0]=postgresql://host1:5432/default
quarkus.datasource.reactive.url[1]=postgresql://host2:5432/default
quarkus.datasource.reactive.url[2]=postgresql://host3:5432/default

Pooled connection idle-timeout

响应式数据源可以使用 `idle-timeout`进行配置。它是连接在池中保持未使用的最长时间,然后再关闭。

`idle-timeout`在默认情况下被禁用。

例如,你可以在 60 分钟后使空闲连接失效:

quarkus.datasource.reactive.idle-timeout=PT60M

Pooled Connection max-lifetime

除了 `idle-timeout`之外,响应式数据源还可以使用 `max-lifetime`进行配置。它是连接在池中保持的最长时间,然后根据需要关闭并替换。`max-lifetime`确保池有最新配置的最新连接。

`max-lifetime`在默认情况下被禁用,但在使用能提供限时凭据的凭据提供程序(如 Vault credentials provider)时,它是重要的配置。

例如,你可以确保连接在 60 分钟后被回收:

quarkus.datasource.reactive.max-lifetime=PT60M

Customizing pool creation

有时,只通过声明无法配置数据库连接池。

例如,您可能必须读取生产中才有的特定文件,或者从专有配置服务器中检索配置数据。

在这种情况下,您可以通过创建实现取决于目标数据库的接口的类来自定义池创建:

Database Pool creator class name

IBM Db2

io.quarkus.reactive.db2.client.DB2PoolCreator

MariaDB/MySQL

io.quarkus.reactive.mysql.client.MySQLPoolCreator

Microsoft SQL Server

io.quarkus.reactive.mssql.client.MSSQLPoolCreator

Oracle

io.quarkus.reactive.oracle.client.OraclePoolCreator

PostgreSQL

io.quarkus.reactive.pg.client.PgPoolCreator

voici un exemple pour PostgreSQL :

import jakarta.inject.Singleton;

import io.quarkus.reactive.pg.client.PgPoolCreator;
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.pgclient.PgPool;
import io.vertx.sqlclient.PoolOptions;

@Singleton
public class CustomPgPoolCreator implements PgPoolCreator {

    @Override
    public PgPool create(Input input) {
        PgConnectOptions connectOptions = input.pgConnectOptions();
        PoolOptions poolOptions = input.poolOptions();
        // Customize connectOptions, poolOptions or both, as required
        return PgPool.pool(input.vertx(), connectOptions, poolOptions);
    }
}

Pipelining

Les clients PostgreSQL et MariaDB/MySQL prennent en charge le traitement pipeliné des requêtes au niveau de la connexion. Cette fonctionnalité consiste à envoyer plusieurs requêtes sur la même connexion à la base de données sans attendre les réponses correspondantes.

Dans certains cas d’utilisation, le traitement en pipeline des requêtes peut améliorer les performances d’accès à la base de données.

voici un exemple pour PostgreSQL :

import jakarta.inject.Inject;

import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.pgclient.PgPool;

public class PipeliningExample {

    @Inject
    PgPool client;

    public Uni<String> favoriteFruitAndVegetable() {
        // Explicitly acquire a connection
        return client.withConnection(conn -> {
            Uni<String> favoriteFruit = conn.query("SELECT name FROM fruits WHERE preferred IS TRUE").execute()
                    .onItem().transform(rows -> rows.iterator().next().getString("name"));
            Uni<String> favoriteVegetable = conn.query("SELECT name FROM vegetables WHERE preferred IS TRUE").execute()
                    .onItem().transform(rows -> rows.iterator().next().getString("name"));
            // favoriteFruit and favoriteVegetable unis will be subscribed at the same time
            return Uni.combine().all().unis(favoriteFruit, favoriteVegetable)
                    .combinedWith(PipeliningExample::formatMessage);
        });
    }

    private static String formatMessage(String fruit, String vegetable) {
        return String.format("The favorite fruit is %s and the favorite vegetable is %s", fruit, vegetable);
    }
}

Le nombre maximal de requêtes en pipeline est configuré avec la propriété pipelining-limit :

# For PostgreSQL
quarkus.datasource.reactive.postgresql.pipelining-limit=256
# For MariaDB/MySQL
quarkus.datasource.reactive.mysql.pipelining-limit=256

Par défaut, pipelining-limit est défini sur 256.

Configuration Reference

Common Datasource

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-datasource.adoc[]

Reactive Datasource

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-reactive-datasource.adoc[]

IBM Db2

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-reactive-db2-client.adoc[]

MariaDB/MySQL

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-reactive-mysql-client.adoc[]

Microsoft SQL Server

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-reactive-mssql-client.adoc[]

Oracle

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-reactive-oracle-client.adoc[]

PostgreSQL

Unresolved include directive in modules/ROOT/pages/reactive-sql-clients.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-reactive-pg-client.adoc[]