Data Access with R2DBC
Package Hierarchy
Spring 框架的 R2DBC 抽象框架包含两个不同的包:
The Spring Framework’s R2DBC abstraction framework consists of two different packages:
-
core
: Theorg.springframework.r2dbc.core
package contains theDatabaseClient
class plus a variety of related classes. See Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling. -
connection
: Theorg.springframework.r2dbc.connection
package contains a utility class for easyConnectionFactory
access and various simpleConnectionFactory
implementations that you can use for testing and running unmodified R2DBC. See Controlling Database Connections.
Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling
本节介绍如何使用 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:
Using DatabaseClient
DatabaseClient
是 R2DBC 核心包中的核心类。它处理资源的创建和释放,这有助于避免常见错误,例如忘记关闭连接。它执行 R2DBC 核心工作流的基本任务(例如语句创建和执行),让应用程序代码提供 SQL 并提取结果。DatabaseClient
类:
DatabaseClient
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. 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:
-
Runs SQL queries
-
Update statements and stored procedure calls
-
Performs iteration over
Result
instances -
Catches R2DBC exceptions and translates them to the generic, more informative, exception hierarchy defined in the
org.springframework.dao
package. (See Consistent Exception Hierarchy.)
该客户端拥有一个函数式流畅式 API,它使用反应式类型进行声明性组合。
The client has a functional, fluent API using reactive types for declarative composition.
当您为您的代码使用 DatabaseClient
时,只需实现 java.util.function
接口即可,给它们一个明确定义的契约。给定由 DatabaseClient
类提供的 Connection
,一个 Function
回调会创建一个 Publisher
。映射函数同样如此,它会提取 Row
结果。
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
引用,在 DAO 实现中使用它,也可以在一个 Spring IoC 容器中配置它,并给它一个 bean 引用。
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.
创建 DatabaseClient
对象最简单的方法是使用一个静态工厂方法,如下所示:
The simplest way to create a DatabaseClient
object is through a static factory method, as follows:
-
Java
-
Kotlin
DatabaseClient client = DatabaseClient.create(connectionFactory);
val client = DatabaseClient.create(connectionFactory)
|
The |
前述的方法会创建一个带有默认设置的 DatabaseClient
。
The preceding method creates a DatabaseClient
with default settings.
您还可以从 DatabaseClient.builder()
获取一个 Builder
实例。您可以通过调用以下方法来自定义客户端:
You can also obtain a Builder
instance from DatabaseClient.builder()
.
You can customize the client by calling the following methods:
-
….bindMarkers(…)
: Supply a specificBindMarkersFactory
to configure named parameter to database bind marker translation. -
….executeFunction(…)
: Set theExecuteFunction
howStatement
objects get run. -
….namedParameters(false)
: Disable named parameter expansion. Enabled by default.
方言由 |
Dialects are resolved by You can let Spring auto-discover your |
目前支持的数据库有:
Currently supported databases are:
-
H2
-
MariaDB
-
Microsoft SQL Server
-
MySQL
-
Postgres
这个类发出的所有 SQL 都会被记录在对应于客户端实例的全限定类名的类别下的 DEBUG
级别(通常为 DefaultDatabaseClient
)。此外,每个执行都会在反应式序列中注册一个检查点,以帮助调试。
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.
以下各节提供了一些 DatabaseClient
使用示例。这些示例不是 DatabaseClient
公开的全部功能的详尽列表。请参阅附带的 javadoc。
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.
Executing Statements
DatabaseClient
提供能运行语句的基本功能。以下示例展示了创建新表所需的最低限度但完全具备功能的代码:
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:
-
Java
-
Kotlin
Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.then();
client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.await()
DatabaseClient
专为方便、流畅的使用而设计。它在执行规范的每个阶段都暴露中间提交、继续和终端方法。上述示例使用 then()
返回一个一旦查询(如果 SQL 查询包含多条语句,则为多个查询)完成就会完成的完成 Publisher
。
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.
|
|
Querying (SELECT
)
SQL 查询可以通过 Row
对象或受影响的行数来返回值。DatabaseClient
可返回更新的行数或行本身,具体取决于发出的查询。
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.
以下查询从表中获取 id
和 name
列:
The following query gets the id
and name
columns from a table:
-
Java
-
Kotlin
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person")
.fetch().first();
val first = client.sql("SELECT id, name FROM person")
.fetch().awaitSingle()
以下查询使用绑定变量:
The following query uses a bind variable:
-
Java
-
Kotlin
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe")
.fetch().first();
val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe")
.fetch().awaitSingle()
您可能注意到了上述示例中 fetch()
的用法。fetch()
是一个继续运算符,它可让您指定需要使用多少数据。
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.
调用 first()
会从结果中返回第一行,并丢弃剩余行。您可以使用以下运算符来使用数据:
Calling first()
returns the first row from the result and discards remaining rows.
You can consume data with the following operators:
-
first()
return the first row of the entire result. Its Kotlin Coroutine variant is namedawaitSingle()
for non-nullable return values andawaitSingleOrNull()
if the value is optional. -
one()
returns exactly one result and fails if the result contains more rows. Using Kotlin Coroutines,awaitOne()
for exactly one value orawaitOneOrNull()
if the value may benull
. -
all()
returns all rows of the result. When using Kotlin Coroutines, useflow()
. -
rowsUpdated()
returns the number of affected rows (INSERT
/UPDATE
/DELETE
count). Its Kotlin Coroutine variant is namedawaitRowsUpdated()
.
在不指定更多映射细节的情况下,查询会将表格结果作为 Map
返回,它的键是不区分大小写的列名,它们映射到各自的列值。
Without specifying further mapping details, queries return tabular results
as Map
whose keys are case-insensitive column names that map to their column value.
您可以为每个 Row
提供 Function<Row, T>
控制结果映射,以便它可以返回任意值(单值、集合和映射以及对象)。
You can take control over result mapping by supplying a Function<Row, T>
that gets
called for each Row
so it can return arbitrary values (singular values,
collections and maps, and objects).
以下示例提取 name
列并发出其值:
The following example extracts the name
column and emits its value:
-
Java
-
Kotlin
Flux<String> names = client.sql("SELECT name FROM person")
.map(row -> row.get("name", String.class))
.all();
val names = client.sql("SELECT name FROM person")
.map{ row: Row -> row.get("name", String.class) }
.flow()
或者,有一个映射到单个值的快捷方式:
Alternatively, there is a shortcut for mapping to a single value:
Flux<String> names = client.sql("SELECT name FROM person")
.mapValue(String.class)
.all();
或者,您可以映射到具有 bean 属性或记录组件的结果对象:
Or you may map to a result object with bean properties or record components:
// assuming a name property on Person
Flux<Person> persons = client.sql("SELECT name FROM person")
.mapProperties(Person.class)
.all();
null
?关系数据库结果可能包含 null
值。Reactive Streams 规范禁止发出 null
值。此要求强制在提取函数中正确处理 null
。虽然您可以从 Row
获取 null
值,但不得发出 null
值。您必须将任何 null
值包装在对象中(例如,Optional
用于单值)以确保您的提取函数绝不会直接返回 null
值。
Relational database results can contain null
values.
The Reactive Streams specification forbids the emission of null
values.
That requirement mandates proper null
handling in the extractor function.
While you can obtain null
values from a Row
, you must not emit a null
value. You must wrap any null
values in an object (for example, Optional
for singular values) to make sure a null
value is never returned directly
by your extractor function.
Updating (INSERT
, UPDATE
, and DELETE
) with DatabaseClient
修改语句的唯一区别在于这些语句通常不返回表格数据,因此您使用 rowsUpdated()
来使用结果。
The only difference of modifying statements is that these statements typically
do not return tabular data so you use rowsUpdated()
to consume results.
以下示例显示返回更新的行数的 UPDATE
语句:
The following example shows an UPDATE
statement that returns the number
of updated rows:
-
Java
-
Kotlin
Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().rowsUpdated();
val affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().awaitRowsUpdated()
Binding Values to Queries
典型的应用程序需要根据一些输入来选择或更新行的参数化 SQL 语句。这些通常是受 WHERE
从句约束的 SELECT
语句,或者接受输入参数的 INSERT
和 UPDATE
语句。如果参数未正确转义,参数化语句会面临 SQL 注入的风险。DatabaseClient
利用 R2DBC 的 bind
API 消除查询参数的 SQL 注入风险。您可以使用 execute(…)
运算符提供参数化 SQL 语句,并将参数绑定到实际的 Statement
。然后,您的 R2DBC 驱动程序将使用准备好的语句和参数替换来运行该语句。
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.
参数绑定支持两种绑定策略:
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:
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34);
或者,您可以传入名称和值映射:
Alternatively, you may pass in a map of names and values:
Map<String, Object> params = new LinkedHashMap<>();
params.put("id", "joe");
params.put("name", "Joe");
params.put("age", 34);
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bindValues(params);
或者,您可以传入具有 bean 属性或记录组件的参数对象:
Or you may pass in a parameter object with bean properties or record components:
// assuming id, name, age properties on Person
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bindProperties(new Person("joe", "Joe", 34);
R2DBC 使用数据库本机绑定标记,这些标记取决于实际的数据库供应商。例如,Postgres 使用索引标记,例如 $1
、$2
、$n
。另一个示例是 SQL Server,它使用以 @
为前缀的命名绑定标记。
R2DBC uses database-native bind markers that depend on the actual database vendor.
As an example, Postgres uses indexed markers, such as $1
, $2
, $n
.
Another example is SQL Server, which uses named bind markers prefixed with @
.
这与 JDBC 不同, JDBC 要求 ?
作为绑定标记。在 JDBC 中,实际的驱动程序会将 ?
绑定标记转换为数据库本机标记,作为其语句执行的一部分。
This is different from JDBC which requires ?
as bind markers.
In JDBC, the actual drivers translate ?
bind markers to database-native
markers as part of their statement execution.
Spring Framework 的 R2DBC 支持允许您使用本机绑定标记或使用 :name
语法命名的绑定标记。
Spring Framework’s R2DBC support lets you use native bind markers or named bind
markers with the :name
syntax.
命名参数支持利用 BindMarkersFactory
实例在查询执行时将命名参数扩展到本机绑定标记,这为您提供了跨各种数据库供应商的特定程度的查询可移植性。
Named parameter support leverages a BindMarkersFactory
instance to expand named
parameters to native bind markers at the time of query execution, which gives you
a certain degree of query portability across various database vendors.
查询预处理器将命名的 Collection
参数展开到一系列绑定标记中,以消除根据参数数量进行动态查询创建的需要。嵌套对象数组被展开,以允许使用(例如)选择列表。
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.
请考虑以下查询:
Consider the following query:
SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50))
前面的查询可以参数化并如下运行:
The preceding query can be parameterized and run as follows:
-
Java
-
Kotlin
List<Object[]> tuples = new ArrayList<>();
tuples.add(new Object[] {"John", 35});
tuples.add(new Object[] {"Ann", 50});
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples);
val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35))
tuples.add(arrayOf("Ann", 50))
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples)
选择列表的使用取决于供应商。 |
Usage of select lists is vendor-dependent. |
以下示例展示了使用 IN
谓词的一个更简单的变体:
The following example shows a simpler variant using IN
predicates:
-
Java
-
Kotlin
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", Arrays.asList(35, 50));
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", arrayOf(35, 50))
R2DBC 本身不支持类集合值。然而,在上面的示例中展开给定的 |
R2DBC itself does not support Collection-like values. Nevertheless,
expanding a given |
Statement Filters
有时你需要对实际的 Statement
进行微调,然后再运行它。要做到这一点,请向 DatabaseClient
注册一个 Statement
过滤器(StatementFilterFunction
)以拦截和修改执行中的语句,如下例所示:
Sometimes you need to fine-tune options on the actual Statement
before it gets run. To do so, register a Statement
filter
(StatementFilterFunction
) with the DatabaseClient
to intercept and
modify statements in their execution, as the following example shows:
-
Java
-
Kotlin
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …)
.bind("state", …);
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
.bind("name", …)
.bind("state", …)
DatabaseClient
还公开了一个简化的 filter(…)
重载,它接受一个 Function<Statement, Statement>
:
DatabaseClient
also exposes a simplified filter(…)
overload that accepts
a Function<Statement, Statement>
:
-
Java
-
Kotlin
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"));
client.sql("SELECT id, name, state FROM table")
.filter(statement -> s.fetchSize(25));
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") }
client.sql("SELECT id, name, state FROM table")
.filter { statement -> s.fetchSize(25) }
StatementFilterFunction
实现允许过滤 Statement
和过滤 Result
对象。
StatementFilterFunction
implementations allow filtering of the
Statement
and filtering of Result
objects.
DatabaseClient
Best Practices
一旦配置好,DatabaseClient
类的实例就会变成线程安全的。这很重要,因为它意味着你可以配置 DatabaseClient
的单个实例,然后将此共享引用安全地注入到多个 DAO(或存储库)中。DatabaseClient
是有状态的,因为它会维护到 ConnectionFactory
的引用,但此状态不是对话状态。
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
类时,一种常见做法是在 Spring 配置文件中配置一个 ConnectionFactory
,然后将共享的 ConnectionFactory
Bean 依赖注入到 DAO 类中。DatabaseClient
在 ConnectionFactory
的 setter 中创建。这样就导致了类似以下内容的 DAO:
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:
-
Java
-
Kotlin
public class R2dbcCorporateEventDao implements CorporateEventDao {
private DatabaseClient databaseClient;
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.databaseClient = DatabaseClient.create(connectionFactory);
}
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao {
private val databaseClient = DatabaseClient.create(connectionFactory)
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
显式配置的替代方法是使用组件扫描和注释来依赖注入。在这种情况下,你可以使用 @Component
注释类(这使其成为组件扫描的候选者),并使用 @Autowired
注释 ConnectionFactory
setter 方法。以下示例演示了如何执行此操作:
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:
- Java
-
@Component (1) public class R2dbcCorporateEventDao implements CorporateEventDao { private DatabaseClient databaseClient; @Autowired (2) public void setConnectionFactory(ConnectionFactory connectionFactory) { this.databaseClient = DatabaseClient.create(connectionFactory); (3) } // R2DBC-backed implementations of the methods on the CorporateEventDao follow... }
1 | Annotate the class with @Component . |
2 | Annotate the ConnectionFactory setter method with @Autowired . |
3 | Create a new DatabaseClient with the ConnectionFactory .
|
4 | Annotate the class with @Component . |
5 | Constructor injection of the ConnectionFactory . |
6 | Create a new DatabaseClient with the ConnectionFactory . |
无论选择使用哪种上述模板初始化样式(或不使用),通常不必在每次想要运行 SQL 时都创建一个新的 DatabaseClient
类实例。一旦配置好,DatabaseClient
实例就是线程安全的。如果你的应用程序访问多个数据库,你可能需要多个 DatabaseClient
实例,这需要多个 ConnectionFactory
以及多个不同配置的 DatabaseClient
实例。
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.
Retrieving Auto-generated Keys
当将行插入定义了自动增量或标识列的表时,INSERT
语句可能会生成键。若要完全控制要生成的列名,只需注册一个请求所需列的生成键的 StatementFilterFunction
。
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.
-
Java
-
Kotlin
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"))
.map(row -> row.get("id", Integer.class))
.first();
// generatedId emits the generated key once the INSERT statement has finished
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") }
.map { row -> row.get("id", Integer.class) }
.awaitOne()
// generatedId emits the generated key once the INSERT statement has finished
Controlling Database Connections
本部分介绍:
This section covers:
Using ConnectionFactory
Spring 通过 ConnectionFactory
获取到与数据库的 R2DBC 连接。ConnectionFactory
是 R2DBC 规范的一部分,是一个通用的驱动程序入口点。它允许容器或框架将连接池和事务管理问题隐藏在应用程序代码中。作为一名开发者,你不需要了解有关如何连接到数据库的详细信息。这是设置 ConnectionFactory
的管理员的责任。在开发和测试代码时,你很可能同时扮演这两个角色,但你不一定需要知道生成数据源是如何配置的。
Spring 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. 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.
当你使用 Spring 的 R2DBC 层时,你可以使用第三方提供的连接池实现来配置自己的连接池。一个流行的实现是 R2DBC Pool(r2dbc-pool
)。Spring 发行版中的实现仅用于测试目的,不提供池。
When you use Spring’s R2DBC layer, you 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.
若要配置 ConnectionFactory
:
To configure a ConnectionFactory
:
-
Obtain a connection with
ConnectionFactory
as you typically obtain an R2DBCConnectionFactory
. -
Provide an R2DBC URL (See the documentation for your driver for the correct value).
以下示例演示了如何配置 ConnectionFactory
:
The following example shows how to configure a ConnectionFactory
:
-
Java
-
Kotlin
ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
val factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
Using ConnectionFactoryUtils
ConnectionFactoryUtils
类是一个方便而强大的帮助类,它提供了 静态 方法,可从 ConnectionFactory
获取连接(必要时)并关闭连接。
The ConnectionFactoryUtils
class is a convenient and powerful helper class
that provides static
methods to obtain connections from ConnectionFactory
and close connections (if necessary).
它支持使用 R2dbcTransactionManager
等订阅者 Context
绑定的连接。
It supports subscriber Context
-bound connections with, for example
R2dbcTransactionManager
.
Using SingleConnectionFactory
SingleConnectionFactory
类是 DelegatingConnectionFactory
接口的一个实现,它包装了一个每次使用后都不关闭的 Connection
。
The SingleConnectionFactory
class is an implementation of DelegatingConnectionFactory
interface that wraps a single Connection
that is not closed after each use.
如果任何客户端代码调用 close
以假设池化连接(例如在使用持久性工具时),则应将 suppressClose
属性设置为 true
。此设置会返回一个关闭抑制代理,该代理包装物理连接。请注意,你不能再将其强制转换为本机 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.
SingleConnectionFactory
主要是一个测试类,如果你允许 R2DBC 驱动程序使用,它可用于特殊要求,如流水线。与池化 ConnectionFactory
相比,它始终重用同一连接,从而避免过多创建物理连接。
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.
Using TransactionAwareConnectionFactoryProxy
TransactionAwareConnectionFactoryProxy
是目标 ConnectionFactory
的代理。代理封装目标 ConnectionFactory
以添加对 Spring 管理的事务的感知。
TransactionAwareConnectionFactoryProxy
is a proxy for a target ConnectionFactory
.
The proxy wraps that target ConnectionFactory
to add awareness of Spring-managed transactions.
如果你使用的 R2DBC 客户端未与 Spring 的 R2DBC 支持集成,则需要使用此类。在这种情况下,你仍然可以使用此客户端,同时让此客户端参与 Spring 管理的事务中。通常最好将 R2DBC 客户端与正确访问 |
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. It is generally
preferable to integrate a R2DBC client with proper access to |
请参阅 TransactionAwareConnectionFactoryProxy
javadoc 了解更多详细信息。
See the TransactionAwareConnectionFactoryProxy
javadoc for more details.
Using R2dbcTransactionManager
R2dbcTransactionManager
类是一个 ReactiveTransactionManager
实现,用于单个 R2DBC ConnectionFactory
。它将 R2DBC Connection
从指定的 ConnectionFactory
绑定到订户 Context
,可能允许每个 ConnectionFactory
有一个订户 Connection
。
The 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
.
应用程序代码需要通过 ConnectionFactoryUtils.getConnection(ConnectionFactory)
而不是 R2DBC 标准的 ConnectionFactory.create()
来检索 R2DBC Connection
。所有框架类(例如 DatabaseClient
)都隐式使用此策略。如果未与事务管理器一起使用,则查找策略的行为与 ConnectionFactory.create()
完全相同,因此在任何情况下都可以使用。
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.