R2DBC ("Reactive Relational Database Connectivity") is a community-driven specification effort to
standardize access to SQL databases using reactive patterns.
R2DBC("反应式关系数据库连接")是一个由社区驱动的规范努力,旨在使用反应式模式标准化对 SQL 数据库的访问。
The Spring Framework’s R2DBC abstraction framework consists of two different packages:
Spring 框架的 R2DBC 抽象框架由两个不同的包组成:
core: The org.springframework.r2dbc.core package contains the DatabaseClient class plus a variety of related
classes. See Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling.
core :该 org.springframework.r2dbc.core 包包含 DatabaseClient 类以及各种相关类。请参阅《使用 R2DBC 核心类控制基本
R2DBC 处理和错误处理》。
connection: The org.springframework.r2dbc.connection package contains a utility class for easy ConnectionFactory
access and various simple ConnectionFactory implementations that you can use for testing and running unmodified
R2DBC. See Controlling Database Connections.
connection : org.springframework.r2dbc.connection 包含一个用于轻松 ConnectionFactory 访问的实用类以及各种简单的
ConnectionFactory 实现,您可以使用这些实现进行测试和运行未经修改的 R2DBC。请参阅控制数据库连接。
4.2. 使用 R2DBC 核心类来控制基本的 R2DBC 处理和错误处理
This section covers how to use the R2DBC core classes to control basic R2DBC processing, including error handling. It
includes the following topics:
本节涵盖如何使用 R2DBC 核心类来控制基本的 R2DBC 处理,包括错误处理。它包括以下主题:
DatabaseClient 4.2.1. 使用DatabaseClientDatabaseClient is the central class in the R2DBC core package. It handles the creation and release of resources, which
helps to avoid common errors, such as forgetting to close the connection.
DatabaseClient 是 R2DBC 核心包中的中心类。它处理资源的创建和释放,有助于避免常见的错误,例如忘记关闭连接。
It performs the basic tasks of the core R2DBC workflow (such as statement creation and execution), leaving application
code to provide SQL and extract results. The DatabaseClient class:
它执行核心 R2DBC 工作流程的基本任务(如语句创建和执行),将 SQL 和提取结果的任务留给应用程序代码。 DatabaseClient 类:
Runs SQL queries 运行 SQL 查询
Update statements and stored procedure calls
更新语句和存储过程调用
Performs iteration over Result instances
执行对 Result 实例的迭代
Catches R2DBC exceptions and translates them to the generic, more informative, exception hierarchy defined in the
org.springframework.dao package. (See Consistent Exception Hierarchy.)
捕获 R2DBC 异常并将它们转换为在 org.springframework.dao 包中定义的通用、更详细的异常层次结构。(见一致的异常层次结构。)
The client has a functional, fluent API using reactive types for declarative composition.
客户端具有使用响应式类型进行声明性组合的功能性、流畅的 API。
When you use the DatabaseClient for your code, you need only to implement java.util.function interfaces, giving them
a clearly defined contract. Given a Connection provided by the DatabaseClient class, a Function callback creates a
Publisher. The same is true for mapping functions that extract a Row result.
当你使用 DatabaseClient 为你的代码时,你只需要实现 java.util.function 接口,并给出一个明确定义的合约。给定由
DatabaseClient 类提供的 Connection ,一个 Function 回调创建一个 Publisher 。映射函数提取 Row 结果时也是如此。
You can use DatabaseClient within a DAO implementation through direct instantiation with a ConnectionFactory
reference, or you can configure it in a Spring IoC container and give it to DAOs as a bean reference.
您可以在 DAO 实现中使用 DatabaseClient ,通过直接使用 ConnectionFactory 引用进行实例化,或者将其配置在 Spring IoC
容器中,并将其作为 bean 引用提供给 DAO。
The simplest way to create a DatabaseClient object is through a static factory method, as follows:
创建 DatabaseClient 对象的最简单方法是通过静态工厂方法,如下所示:
The ConnectionFactory should always be configured as a bean in the Spring IoC container.
ConnectionFactory 应始终在 Spring IoC 容器中配置为 Bean。
The preceding method creates a DatabaseClient with default settings.
前述方法使用默认设置创建了一个 DatabaseClient 。
You can also obtain a Builder instance from DatabaseClient.builder(). You can customize the client by calling the
following methods:
您也可以从 DatabaseClient.builder() 获取一个 Builder 实例。您可以通过调用以下方法来自定义客户端:
….bindMarkers(…): Supply a specific BindMarkersFactory to configure named parameter to database bind marker
translation.
….bindMarkers(…) : 提供特定的 BindMarkersFactory 以配置命名参数到数据库绑定标记的翻译。
….executeFunction(…): Set the ExecuteFunction how Statement objects get run.
….executeFunction(…) : 设置 ExecuteFunction 对象如何运行。
….namedParameters(false): Disable named parameter expansion. Enabled by default.
….namedParameters(false) : 禁用命名参数展开。默认启用。
Dialects are resolved by
BindMarkersFactoryResolver
from a ConnectionFactory, typically by inspecting ConnectionFactoryMetadata.
方言通过 BindMarkersFactoryResolver 从 ConnectionFactory 中解析,通常通过检查 ConnectionFactoryMetadata 。
You can let Spring auto-discover your BindMarkersFactory by registering a class that implements
org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver$BindMarkerFactoryProvider through
META-INF/spring.factories. BindMarkersFactoryResolver discovers bind marker provider implementations from the class
path using Spring’s SpringFactoriesLoader.
您可以允许 Spring 通过注册一个通过 META-INF/spring.factories 实现的
org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver$BindMarkerFactoryProvider 的类来自动发现您的
BindMarkersFactory 。 BindMarkersFactoryResolver 使用 Spring 的 SpringFactoriesLoader 从类路径中发现绑定标记提供者实现。
Currently supported databases are:
当前支持的数据库有:
H2
MariaDB
Microsoft SQL Server 微软 SQL Server
MySQL
Postgres PostgreSQL
All SQL issued by this class is logged at the DEBUG level under the category corresponding to the fully qualified
class name of the client instance (typically DefaultDatabaseClient). Additionally, each execution registers a
checkpoint in the reactive sequence to aid debugging.
所有由此类发出的 SQL 都在对应客户端实例完全限定类名的类别下以 DEBUG 级别进行记录(通常是 DefaultDatabaseClient
)。此外,每次执行还会在反应序列中注册一个检查点以帮助调试。
The following sections provide some examples of DatabaseClient usage. These examples are not an exhaustive list of all
of the functionality exposed by the DatabaseClient. See the
attendant javadoc
for that.
以下部分提供了一些 DatabaseClient 的使用示例。这些示例并不是 DatabaseClient 所暴露的所有功能的完整列表。有关详细信息,请参阅相关的
javadoc。
DatabaseClient provides the basic functionality of running a statement. The following example shows what you need to
include for minimal but fully functional code that creates a new table:
DatabaseClient 提供了运行语句的基本功能。以下示例展示了创建一个新表所需包含的最小但功能完整的代码:
DatabaseClient is designed for convenient, fluent usage. It exposes intermediate, continuation, and terminal methods
at each stage of the execution specification. The preceding example above uses then() to return a completion
Publisher that completes as soon as the query (or queries, if the SQL query contains multiple statements) completes.
DatabaseClient 设计用于方便流畅的使用。它在执行规范的每个阶段都公开了中间、延续和终端方法。上述示例使用 then() 返回一个完成
Publisher ,一旦查询(或查询,如果 SQL 查询包含多个语句)完成,它就会立即完成。
execute(…) accepts either the SQL query string or a query Supplier<String> to defer the actual query creation until
execution.
execute(…) 接受 SQL 查询字符串或延迟到执行时再创建查询的查询 Supplier<String> 。
SELECT) 查询(SELECT )SQL queries can return values through Row objects or the number of affected rows. DatabaseClient can return the
number of updated rows or the rows themselves, depending on the issued query.
SQL 查询可以通过 Row 对象或受影响行数返回值。 DatabaseClient 可以返回更新行数或行本身,具体取决于所执行的查询。
The following query gets the id and name columns from a table:
以下查询从表中获取 id 和 name 列:
The following query uses a bind variable:
以下查询使用了一个绑定变量:
You might have noticed the use of fetch() in the example above. fetch() is a continuation operator that lets you
specify how much data you want to consume.
您可能已经注意到上述示例中使用了 fetch() 。 fetch() 是一个延续操作符,允许您指定想要消耗多少数据。
Calling first() returns the first row from the result and discards remaining rows. You can consume data with the
following operators:
调用 first() 返回结果的第一行并丢弃剩余的行。您可以使用以下运算符来消费数据:
first() return the first row of the entire result. Its Kotlin Coroutine variant is named awaitSingle() for
non-nullable return values and awaitSingleOrNull() if the value is optional.
first() 返回整个结果的第一行。其 Kotlin 协程变体名为 awaitSingle() 用于非空返回值, awaitSingleOrNull() 如果值是可选的。
one() returns exactly one result and fails if the result contains more rows. Using Kotlin Coroutines, awaitOne()
for exactly one value or awaitOneOrNull() if the value may be null.
one() 返回精确一个结果,如果结果包含多行则失败。使用 Kotlin 协程, awaitOne() 用于精确一个值或 awaitOneOrNull()
如果值可能是 null 。
all() returns all rows of the result. When using Kotlin Coroutines, use flow().
all() 返回所有结果行。当使用 Kotlin 协程时,使用 flow() 。
rowsUpdated() returns the number of affected rows (INSERT/UPDATE/DELETE count). Its Kotlin Coroutine variant
is named awaitRowsUpdated().
rowsUpdated() 返回受影响行数( INSERT / UPDATE / DELETE 计数)。其 Kotlin 协程变体名为 awaitRowsUpdated() 。
Without specifying further mapping details, queries return tabular results as Map whose keys are case-insensitive
column names that map to their column value.
未指定进一步的映射细节,查询返回表格结果 Map ,其键为不区分大小写的列名,映射到它们的列值。
You can take control over result mapping by supplying a Function<Row, T> that gets called for each Row so it can can
return arbitrary values (singular values, collections and maps, and objects).
您可以通过提供每个 Row 调用的 Function<Row, T> 来控制结果映射,使其可以返回任意值(单个值、集合和映射以及对象)。
The following example extracts the name column and emits its value:
以下示例提取了 name 列并发出其值:
INSERT,UPDATE, andDELETE) withDatabaseClient更新( INSERT , UPDATE ,和 DELETE )为 DatabaseClient
The only difference of modifying statements is that these statements typically do not return tabular data so you use
rowsUpdated() to consume results.
修改语句的唯一区别是,这些语句通常不返回表格数据,因此您使用 rowsUpdated() 来消费结果。
The following example shows an UPDATE statement that returns the number of updated rows:
以下示例显示了一个返回更新行数的 UPDATE 语句:
绑定值到查询
A typical application requires parameterized SQL statements to select or update rows according to some input. These are
typically SELECT statements constrained by a WHERE clause or INSERT and UPDATE statements that accept input
parameters. Parameterized statements bear the risk of SQL injection if parameters are not escaped properly.
DatabaseClient leverages R2DBC’s bind API to eliminate the risk of SQL injection for query parameters. You can
provide a parameterized SQL statement with the execute(…) operator and bind parameters to the actual Statement. Your
R2DBC driver then runs the statement by using prepared statements and parameter substitution.
一个典型应用需要参数化 SQL 语句来根据某些输入选择或更新行。这些通常是受 WHERE 子句或 INSERT 和 UPDATE 语句约束的
SELECT 语句,这些语句接受输入参数。如果参数没有正确转义,参数化语句会存在 SQL 注入的风险。 DatabaseClient 利用 R2DBC 的
bind API 消除查询参数的 SQL 注入风险。您可以使用 execute(…) 运算符提供参数化 SQL 语句,并将参数绑定到实际的
Statement 。然后,您的 R2DBC 驱动程序通过使用预定义语句和参数替换来运行该语句。
Parameter binding supports two binding strategies:
参数绑定支持两种绑定策略:
By Index, using zero-based parameter indexes.
通过索引,使用基于零的参数索引。
By Name, using the placeholder name.
按名称,使用占位符名称。
The following example shows parameter binding for a query:
以下示例展示了查询的参数绑定:
The query-preprocessor unrolls named Collection parameters into a series of bind markers to remove the need of dynamic
query creation based on the number of arguments. Nested object arrays are expanded to allow usage of (for example)
select lists.
查询预处理器将命名参数 Collection 展开成一系列绑定标记,以消除基于参数数量动态创建查询的需求。嵌套对象数组被展开,以允许使用(例如)选择列表。
Consider the following query:
考虑以下查询:
The preceding query can be parametrized and run as follows:
前一个查询可以参数化并按以下方式运行:
Usage of select lists is vendor-dependent.
选择列表的使用取决于供应商。
The following example shows a simpler variant using IN predicates:
以下示例展示了使用 IN 谓词的简化变体:
R2DBC itself does not support Collection-like values. Nevertheless, expanding a given List in the example above works
for named parameters in Spring’s R2DBC support, e.g. for use in IN clauses as shown above. However, inserting or
updating array-typed columns (e.g. in Postgres) requires an array type that is supported by the underlying R2DBC driver:
typically a Java array, e.g. String to update a text column. Do not pass Collection<String> or the like as an
array parameter.
R2DBC 本身不支持类似 Collection 的值。然而,在上面的示例中,给定 List 的展开对于 Spring 的 R2DBC 支持中的命名参数是有效的,例如在上面的
IN 子句中使用。但是,插入或更新数组类型的列(例如在 Postgres 中)需要底层 R2DBC 驱动支持的数组类型:通常是 Java 数组,例如
String 更新 text 列。不要将 Collection<String> 或类似内容作为数组参数传递。
Sometimes it you need to fine-tune options on the actual Statement before it gets run. Register a Statement filter (
StatementFilterFunction) through DatabaseClient to intercept and modify statements in their execution, as the
following example shows:
有时在运行之前,您可能需要调整实际的 Statement 选项。通过 DatabaseClient 注册一个 Statement 过滤器(
StatementFilterFunction ),以拦截并修改执行中的语句,如下例所示:
DatabaseClient exposes also simplified filter(…) overload accepting Function<Statement, Statement>:
DatabaseClient 还暴露了接受 Function<Statement, Statement> 的简化版 filter(…) 载荷
StatementFilterFunction implementations allow filtering of the Statement and filtering of Result objects.
StatementFilterFunction 实现允许过滤 Statement 和过滤 Result 对象。
DatabaseClient Best PracticesDatabaseClient 最佳实践Instances of the DatabaseClient class are thread-safe, once configured. This is important because it means that you
can configure a single instance of a DatabaseClient and then safely inject this shared reference into multiple DAOs (
or repositories). The DatabaseClient is stateful, in that it maintains a reference to a ConnectionFactory, but this
state is not conversational state.
该 DatabaseClient 类的实例一旦配置即线程安全。这很重要,因为它意味着您可以配置一个 DatabaseClient
的单个实例,然后将这个共享引用安全地注入到多个 DAO(或存储库)中。 DatabaseClient 是有状态的,因为它维护了对
ConnectionFactory 的引用,但这种状态不是对话状态。
A common practice when using the DatabaseClient class is to configure a ConnectionFactory in your Spring
configuration file and then dependency-inject that shared ConnectionFactory bean into your DAO classes. The
DatabaseClient is created in the setter for the ConnectionFactory. This leads to DAOs that resemble the following:
使用 DatabaseClient 类时的常见做法是在 Spring 配置文件中配置一个 ConnectionFactory ,然后将这个共享的
ConnectionFactory bean 依赖注入到 DAO 类中。 DatabaseClient 在 ConnectionFactory 的 setter 方法中创建。这导致 DAO
类类似于以下结构:
1
Annotate the class with @Component.
为类添加 @Component 注解。
2
Annotate the ConnectionFactory setter method with @Autowired.
注释 ConnectionFactory 设置方法为 @Autowired 。
3
Create a new DatabaseClient with the ConnectionFactory.
创建一个新的 DatabaseClient 使用 ConnectionFactory 。
An alternative to explicit configuration is to use component-scanning and annotation support for dependency injection.
In this case, you can annotate the class with @Component (which makes it a candidate for component-scanning) and
annotate the ConnectionFactory setter method with @Autowired. The following example shows how to do so:
显式配置的替代方案是使用组件扫描和注解支持来实现依赖注入。在这种情况下,您可以使用 @Component 注解类(使其成为组件扫描的候选者),并使用
@Autowired 注解 ConnectionFactory 设置方法。以下示例展示了如何这样做:
1
Annotate the class with @Component.
为类添加 @Component 注解。
2
Constructor injection of the ConnectionFactory.
构造函数注入 ConnectionFactory 。
3
Create a new DatabaseClient with the ConnectionFactory.
创建一个新的 DatabaseClient 使用 ConnectionFactory 。
Regardless of which of the above template initialization styles you choose to use (or not), it is seldom necessary to
create a new instance of a DatabaseClient class each time you want to run SQL. Once configured, a DatabaseClient
instance is thread-safe. If your application accesses multiple databases, you may want multiple DatabaseClient
instances, which requires multiple ConnectionFactory and, subsequently, multiple differently configured
DatabaseClient instances.
无论选择哪种模板初始化样式(或不选择),在每次运行 SQL 时创建一个新的 DatabaseClient 类实例通常是不必要的。一旦配置完成,
DatabaseClient 实例是线程安全的。如果您的应用程序访问多个数据库,您可能需要多个 DatabaseClient 实例,这需要多个
ConnectionFactory ,随后是多个配置不同的 DatabaseClient 实例。
4.3. 获取自动生成的键
INSERT statements may generate keys when inserting rows into a table that defines an auto-increment or identity
column. To get full control over the column name to generate, simply register a StatementFilterFunction that requests
the generated key for the desired column.
INSERT 语句在向定义了自增或标识列的表中插入行时可能会生成键。要完全控制要生成的列名,只需注册一个
StatementFilterFunction 请求所需列的生成键。
4.4. 控制数据库连接
This section covers:本节涵盖:
ConnectionFactory 4.4.1. 使用ConnectionFactorySpring obtains an R2DBC connection to the database through a ConnectionFactory. A ConnectionFactory is part of the
R2DBC specification and is a common entry-point for drivers. It lets a container or a framework hide connection pooling
and transaction management issues from the application code. As a developer, you need not know details about how to
connect to the database.
Spring 通过 ConnectionFactory 从数据库获取 R2DBC 连接。 ConnectionFactory 是 R2DBC
规范的一部分,是驱动程序的通用入口点。它允许容器或框架将连接池和事务管理问题从应用程序代码中隐藏起来。作为开发者,你不需要了解如何连接到数据库的细节。
That is the responsibility of the administrator who sets up the ConnectionFactory. You most likely fill both roles as
you develop and test code, but you do not necessarily have to know how the production data source is configured.
这是设置 ConnectionFactory 的管理员的职责。在开发和测试代码时,你很可能同时扮演这两个角色,但你不必一定了解生产数据源是如何配置的。
When you use Spring’s R2DBC layer, you can can configure your own with a connection pool implementation provided by a
third party. A popular implementation is R2DBC Pool (r2dbc-pool). Implementations in the Spring distribution are meant
only for testing purposes and do not provide pooling.
当您使用 Spring 的 R2DBC 层时,您可以使用第三方提供的连接池实现来自定义配置。一个流行的实现是 R2DBC Pool( r2dbc-pool
)。Spring 发行版中的实现仅用于测试目的,不提供连接池功能。
To configure a ConnectionFactory:
配置一个 ConnectionFactory :
Obtain a connection with ConnectionFactory as you typically obtain an R2DBC ConnectionFactory.
获取与 ConnectionFactory 的连接,就像您通常获取一个 R2DBC ConnectionFactory 连接一样。
Provide an R2DBC URL (See the documentation for your driver for the correct value).
提供 R2DBC URL(请参阅您驱动程序的文档以获取正确值)。
The following example shows how to configure a ConnectionFactory:
以下示例展示了如何配置一个 ConnectionFactory :
ConnectionFactoryUtils 4.4.2. 使用ConnectionFactoryUtilsThe ConnectionFactoryUtils class is a convenient and powerful helper class that provides static methods to obtain
connections from ConnectionFactory and close connections (if necessary).
ConnectionFactoryUtils 类是一个方便且强大的辅助类,它提供了从 ConnectionFactory 获取连接和(如果需要)关闭连接的
static 方法。
It supports subscriber Context-bound connections with, for example R2dbcTransactionManager.
它支持与 Context -绑定连接,例如 R2dbcTransactionManager 。
SingleConnectionFactory 4.4.3. 使用SingleConnectionFactoryThe SingleConnectionFactory class is an implementation of DelegatingConnectionFactory interface that wraps a single
Connection that is not closed after each use.
SingleConnectionFactory 类是实现 DelegatingConnectionFactory 接口的一个封装,每次使用后不会关闭单个 Connection 。
If any client code calls close on the assumption of a pooled connection (as when using persistence tools), you should
set the suppressClose property to true. This setting returns a close-suppressing proxy that wraps the physical
connection. Note that you can no longer cast this to a native Connection or a similar object.
如果任何客户端代码在假设使用连接池的情况下调用 close (例如在使用持久化工具时),您应将 suppressClose 属性设置为 true
。此设置返回一个包装物理连接的抑制关闭代理。请注意,您不能再将其转换为原生 Connection 或类似对象。
SingleConnectionFactory is primarily a test class and may be used for specific requirements such as pipelining if your
R2DBC driver permits for such use. In contrast to a pooled ConnectionFactory, it reuses the same connection all the
time, avoiding excessive creation of physical connections.
SingleConnectionFactory 主要是一个测试类,如果您的 R2DBC 驱动程序允许,可以用于特定的要求,如流水线。与池化的
ConnectionFactory 相比,它始终重用相同的连接,避免过度创建物理连接。
TransactionAwareConnectionFactoryProxy 4.4.4. 使用TransactionAwareConnectionFactoryProxy
TransactionAwareConnectionFactoryProxy is a proxy for a target ConnectionFactory. The proxy wraps that target
ConnectionFactory to add awareness of Spring-managed transactions.
TransactionAwareConnectionFactoryProxy 是目标 ConnectionFactory 的代理。代理封装该目标 ConnectionFactory 以增加对
Spring 管理的事务的意识。
Using this class is required if you use a R2DBC client that is not integrated otherwise with Spring’s R2DBC support. In
this case, you can still use this client and, at the same time, have this client participating in Spring managed
transactions.
使用此类是必需的,如果您使用的是未与 Spring 的 R2DBC 支持集成的 R2DBC 客户端。在这种情况下,您仍然可以使用此客户端,同时让此客户端参与
Spring 管理的交易。
It is generally preferable to integrate a R2DBC client with proper access to ConnectionFactoryUtils for resource
management.
通常更倾向于将 R2DBC 客户端与对 ConnectionFactoryUtils 的适当访问集成,以实现资源管理。
R2dbcTransactionManager 4.4.5. 使用R2dbcTransactionManagerThe R2dbcTransactionManager class is a ReactiveTransactionManager implementation for a single R2DBC
ConnectionFactory. It binds an R2DBC Connection from the specified ConnectionFactory to the subscriber Context,
potentially allowing for one subscriber Connection for each ConnectionFactory.
R2dbcTransactionManager 类是一个针对单个 R2DBC ReactiveTransactionManager 的 ConnectionFactory 实现。它将指定的
Connection 绑定到订阅者 ConnectionFactory ,可能允许每个 Context 有一个订阅者 Connection 。
Application code is required to retrieve the R2DBC Connection through
ConnectionFactoryUtils.getConnection(ConnectionFactory), instead of R2DBC’s standard ConnectionFactory.create(). All
framework classes (such as DatabaseClient) use this strategy implicitly. If not used with a transaction manager, the
lookup strategy behaves exactly like ConnectionFactory.create() and can therefore be used in any case.
应用程序代码需要通过 Connection 至 ConnectionFactoryUtils.getConnection(ConnectionFactory) 获取 R2DBC,而不是使用
R2DBC 的标准 ConnectionFactory.create() 。所有框架类(如 DatabaseClient )都隐式地使用这种策略。如果不与事务管理器一起使用,查找策略的行为与
ConnectionFactory.create() 完全相同,因此可以在任何情况下使用。