Merge branch '6.0.x'

This commit is contained in:
Juergen Hoeller 2023-07-05 12:53:07 +02:00
commit 1218e65ca1
12 changed files with 143 additions and 139 deletions

View File

@ -151,10 +151,10 @@ Kotlin::
---- ----
====== ======
The last example we show here is for typical JDBC support. You could have the The last example we show here is for typical JDBC support. You could have the `DataSource`
`DataSource` injected into an initialization method or a constructor, where you would create a injected into an initialization method or a constructor, where you would create a `JdbcTemplate`
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using and other data access support classes (such as `SimpleJdbcCall` and others) by using this
this `DataSource`. The following example autowires a `DataSource`: `DataSource`. The following example autowires a `DataSource`:
[tabs] [tabs]
====== ======

View File

@ -9,13 +9,13 @@ to the database.
[[jdbc-batch-classic]] [[jdbc-batch-classic]]
== Basic Batch Operations with `JdbcTemplate` == Basic Batch Operations with `JdbcTemplate`
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special You accomplish `JdbcTemplate` batch processing by implementing two methods of a special interface,
interface, `BatchPreparedStatementSetter`, and passing that implementation in as the second parameter `BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
in your `batchUpdate` method call. You can use the `getBatchSize` method to provide the size of in your `batchUpdate` method call. You can use the `getBatchSize` method to provide the size of
the current batch. You can use the `setValues` method to set the values for the parameters of the current batch. You can use the `setValues` method to set the values for the parameters of
the prepared statement. This method is called the number of times that you the prepared statement. This method is called the number of times that you specified in the
specified in the `getBatchSize` call. The following example updates the `t_actor` table `getBatchSize` call. The following example updates the `t_actor` table based on entries in a list,
based on entries in a list, and the entire list is used as the batch: and the entire list is used as the batch:
[tabs] [tabs]
====== ======

View File

@ -10,7 +10,7 @@ This section covers:
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`] * xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`] * xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`] * xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager`] * xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]
[[jdbc-datasource]] [[jdbc-datasource]]
@ -125,8 +125,12 @@ The following example shows C3P0 configuration:
== Using `DataSourceUtils` == Using `DataSourceUtils`
The `DataSourceUtils` class is a convenient and powerful helper class that provides The `DataSourceUtils` class is a convenient and powerful helper class that provides
`static` methods to obtain connections from JNDI and close connections if necessary. It `static` methods to obtain connections from JNDI and close connections if necessary.
supports thread-bound connections with, for example, `DataSourceTransactionManager`. It supports a thread-bound JDBC `Connection` with `DataSourceTransactionManager` but
also with `JtaTransactionManager` and `JpaTransactionManager`.
Note that `JdbcTemplate` implies `DataSourceUtils` connection access, using it
behind every JDBC operation, implicitly participating in an ongoing transaction.
[[jdbc-SmartDataSource]] [[jdbc-SmartDataSource]]
@ -165,7 +169,6 @@ In contrast to `DriverManagerDataSource`, it reuses the same connection all the
avoiding excessive creation of physical connections. avoiding excessive creation of physical connections.
[[jdbc-DriverManagerDataSource]] [[jdbc-DriverManagerDataSource]]
== Using `DriverManagerDataSource` == Using `DriverManagerDataSource`
@ -201,29 +204,44 @@ javadoc for more details.
[[jdbc-DataSourceTransactionManager]] [[jdbc-DataSourceTransactionManager]]
== Using `DataSourceTransactionManager` == Using `DataSourceTransactionManager` / `JdbcTransactionManager`
The `DataSourceTransactionManager` class is a `PlatformTransactionManager` The `DataSourceTransactionManager` class is a `PlatformTransactionManager`
implementation for single JDBC data sources. It binds a JDBC connection from the implementation for a single JDBC `DataSource`. It binds a JDBC `Connection`
specified data source to the currently executing thread, potentially allowing for one from the specified `DataSource` to the currently executing thread, potentially
thread connection per data source. allowing for one thread-bound `Connection` per `DataSource`.
Application code is required to retrieve the JDBC connection through Application code is required to retrieve the JDBC `Connection` through
`DataSourceUtils.getConnection(DataSource)` instead of Jakarta EE's standard `DataSourceUtils.getConnection(DataSource)` instead of Java EE's standard
`DataSource.getConnection`. It throws unchecked `org.springframework.dao` exceptions `DataSource.getConnection`. It throws unchecked `org.springframework.dao` exceptions
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use this instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use
strategy implicitly. If not used with this transaction manager, the lookup strategy this strategy implicitly. If not used with a transaction manager, the lookup strategy
behaves exactly like the common one. Thus, it can be used in any case. behaves exactly like `DataSource.getConnection` and can therefore be used in any case.
The `DataSourceTransactionManager` class supports custom isolation levels and timeouts The `DataSourceTransactionManager` class supports savepoints (`PROPAGATION_NESTED`),
that get applied as appropriate JDBC statement query timeouts. To support the latter, custom isolation levels, and timeouts that get applied as appropriate JDBC statement
application code must either use `JdbcTemplate` or call the query timeouts. To support the latter, application code must either use `JdbcTemplate` or
`DataSourceUtils.applyTransactionTimeout(..)` method for each created statement. call the `DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.
You can use this implementation instead of `JtaTransactionManager` in the single-resource You can use `DataSourceTransactionManager` instead of `JtaTransactionManager` in the
case, as it does not require the container to support JTA. Switching between single-resource case, as it does not require the container to support a JTA transaction
both is just a matter of configuration, provided you stick to the required connection lookup coordinator. Switching between these transaction managers is just a matter of configuration,
pattern. JTA does not support custom isolation levels. provided you stick to the required connection lookup pattern. Note that JTA does not support
savepoints or custom isolation levels and has a different timeout mechanism but otherwise
exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.
NOTE: As of 5.3, Spring provides an extended `JdbcTransactionManager` variant which adds
exception translation capabilities on commit/rollback (aligned with `JdbcTemplate`).
Where `DataSourceTransactionManager` will only ever throw `TransactionSystemException`
(analogous to JTA), `JdbcTransactionManager` translates database locking failures etc to
corresponding `DataAccessException` subclasses. Note that application code needs to be
prepared for such exceptions, not exclusively expecting `TransactionSystemException`.
In scenarios where that is the case, `JdbcTransactionManager` is the recommended choice.
In terms of exception behavior, `JdbcTransactionManager` is roughly equivalent to
`JpaTransactionManager` and also to `R2dbcTransactionManager`, serving as an immediate
companion/replacement for each other. `DataSourceTransactionManager` on the other hand
is equivalent to `JtaTransactionManager` and can serve as a direct replacement there.

View File

@ -718,12 +718,22 @@ See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate`
between ``SQLException``s and Spring's own `org.springframework.dao.DataAccessException`, between ``SQLException``s and Spring's own `org.springframework.dao.DataAccessException`,
which is agnostic in regard to data access strategy. Implementations can be generic (for which is agnostic in regard to data access strategy. Implementations can be generic (for
example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error
codes) for greater precision. codes) for greater precision. This exception translation mechanism is used behind the
the common `JdbcTemplate` and `JdbcTransactionManager` entry points which do not
propagate `SQLException` but rather `DataAccessException`.
NOTE: As of 6.0, the default exception translator is `SQLExceptionSubclassTranslator`,
detecting JDBC 4 `SQLException` subclasses with a few extra checks, and with a fallback
to `SQLState` introspection through `SQLStateSQLExceptionTranslator`. This is usually
sufficient for common database access and does not require vendor-specific detection.
For backwards compatibility, consider using `SQLErrorCodeSQLExceptionTranslator` as
described below, potentially with custom error code mappings.
`SQLErrorCodeSQLExceptionTranslator` is the implementation of `SQLExceptionTranslator` `SQLErrorCodeSQLExceptionTranslator` is the implementation of `SQLExceptionTranslator`
that is used by default. This implementation uses specific vendor codes. It is more that is used by default when a file named `sql-error-codes.xml` is present in the root
precise than the `SQLState` implementation. The error code translations are based on of the classpath. This implementation uses specific vendor codes. It is more precise than
codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and `SQLState` or `SQLException` subclass translation. The error code translations are based
on codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
populated by an `SQLErrorCodesFactory`, which (as the name suggests) is a factory for populated by an `SQLErrorCodesFactory`, which (as the name suggests) is a factory for
creating `SQLErrorCodes` based on the contents of a configuration file named creating `SQLErrorCodes` based on the contents of a configuration file named
`sql-error-codes.xml`. This file is populated with vendor codes and based on the `sql-error-codes.xml`. This file is populated with vendor codes and based on the
@ -744,8 +754,8 @@ The `SQLErrorCodeSQLExceptionTranslator` applies matching rules in the following
translator. If this translation is not available, the next fallback translator is translator. If this translation is not available, the next fallback translator is
the `SQLStateSQLExceptionTranslator`. the `SQLStateSQLExceptionTranslator`.
NOTE: The `SQLErrorCodesFactory` is used by default to define `Error` codes and custom exception NOTE: The `SQLErrorCodesFactory` is used by default to define error codes and custom
translations. They are looked up in a file named `sql-error-codes.xml` from the exception translations. They are looked up in a file named `sql-error-codes.xml` from the
classpath, and the matching `SQLErrorCodes` instance is located based on the database classpath, and the matching `SQLErrorCodes` instance is located based on the database
name from the database metadata of the database in use. name from the database metadata of the database in use.
@ -784,12 +794,12 @@ Kotlin::
---- ----
====== ======
In the preceding example, the specific error code (`-12345`) is translated, while other errors are In the preceding example, the specific error code (`-12345`) is translated while
left to be translated by the default translator implementation. To use this custom other errors are left to be translated by the default translator implementation.
translator, you must pass it to the `JdbcTemplate` through the method To use this custom translator, you must pass it to the `JdbcTemplate` through the
`setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the data access method `setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the
processing where this translator is needed. The following example shows how you can use this custom data access processing where this translator is needed. The following example shows
translator: how you can use this custom translator:
[tabs] [tabs]
====== ======
@ -800,7 +810,6 @@ Java::
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) { public void setDataSource(DataSource dataSource) {
// create a JdbcTemplate and set data source // create a JdbcTemplate and set data source
this.jdbcTemplate = new JdbcTemplate(); this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource); this.jdbcTemplate.setDataSource(dataSource);
@ -809,7 +818,6 @@ Java::
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator(); CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource); tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr); this.jdbcTemplate.setExceptionTranslator(tr);
} }
public void updateShippingCharge(long orderId, long pct) { public void updateShippingCharge(long orderId, long pct) {

View File

@ -3,30 +3,30 @@
The Spring Framework's JDBC abstraction framework consists of four different packages: The Spring Framework's JDBC abstraction framework consists of four different packages:
* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class and its * `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class
various callback interfaces, plus a variety of related classes. A subpackage named and its various callback interfaces, plus a variety of related classes. A subpackage
`org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and named `org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
`SimpleJdbcCall` classes. Another subpackage named `SimpleJdbcCall` classes. Another subpackage named
`org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate` `org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate`
class and the related support classes. See xref:data-access/jdbc/core.adoc[Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling], xref:data-access/jdbc/advanced.adoc[JDBC Batch Operations], and class and the related support classes. See xref:data-access/jdbc/core.adoc[Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling], xref:data-access/jdbc/advanced.adoc[JDBC Batch Operations], and
xref:data-access/jdbc/simple.adoc[Simplifying JDBC Operations with the `SimpleJdbc` Classes]. xref:data-access/jdbc/simple.adoc[Simplifying JDBC Operations with the `SimpleJdbc` Classes].
* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class for easy * `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class
`DataSource` access and various simple `DataSource` implementations that you can use for for easy `DataSource` access and various simple `DataSource` implementations that you can
testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage use for testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
named `org.springfamework.jdbc.datasource.embedded` provides support for creating named `org.springframework.jdbc.datasource.embedded` provides support for creating
embedded databases by using Java database engines, such as HSQL, H2, and Derby. See embedded databases by using Java database engines, such as HSQL, H2, and Derby. See
xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support]. xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support].
* `object`: The `org.springframework.jdbc.object` package contains classes that represent RDBMS * `object`: The `org.springframework.jdbc.object` package contains classes that represent
queries, updates, and stored procedures as thread-safe, reusable objects. See RDBMS queries, updates, and stored procedures as thread-safe, reusable objects. See
xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This approach is modeled by JDO, although objects returned by queries xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This approach is modeled by JDO, although objects returned by queries
are naturally disconnected from the database. This higher-level of JDBC abstraction are naturally disconnected from the database. This higher-level of JDBC abstraction
depends on the lower-level abstraction in the `org.springframework.jdbc.core` package. depends on the lower-level abstraction in the `org.springframework.jdbc.core` package.
* `support`: The `org.springframework.jdbc.support` package provides `SQLException` translation * `support`: The `org.springframework.jdbc.support` package provides `SQLException`
functionality and some utility classes. Exceptions thrown during JDBC processing are translation functionality and some utility classes. Exceptions thrown during JDBC processing
translated to exceptions defined in the `org.springframework.dao` package. This means are translated to exceptions defined in the `org.springframework.dao` package. This means
that code using the Spring JDBC abstraction layer does not need to implement JDBC or that code using the Spring JDBC abstraction layer does not need to implement JDBC or
RDBMS-specific error handling. All translated exceptions are unchecked, which gives you RDBMS-specific error handling. All translated exceptions are unchecked, which gives you
the option of catching the exceptions from which you can recover while letting other the option of catching the exceptions from which you can recover while letting other

View File

@ -495,7 +495,7 @@ features supported by Spring, usually in a vendor-specific manner:
* Applying specific transaction semantics (such as custom isolation level or transaction * Applying specific transaction semantics (such as custom isolation level or transaction
timeout) timeout)
* Retrieving the transactional JDBC `Connection` (for exposure to JDBC-based DAOs) * Retrieving the transactional JDBC `Connection` (for exposure to JDBC-based DAOs)
* Advanced translation of `PersistenceExceptions` to Spring `DataAccessExceptions` * Advanced translation of `PersistenceException` to Spring's `DataAccessException`
This is particularly valuable for special transaction semantics and for advanced This is particularly valuable for special transaction semantics and for advanced
translation of exception. The default implementation (`DefaultJpaDialect`) does translation of exception. The default implementation (`DefaultJpaDialect`) does

View File

@ -718,19 +718,15 @@ javadoc for more details.
=== Using `R2dbcTransactionManager` === Using `R2dbcTransactionManager`
The `R2dbcTransactionManager` class is a `ReactiveTransactionManager` implementation for The `R2dbcTransactionManager` class is a `ReactiveTransactionManager` implementation for
single R2DBC data sources. It binds an R2DBC connection from the specified connection factory a single R2DBC `ConnectionFactory`. It binds an R2DBC `Connection` from the specified
to the subscriber `Context`, potentially allowing for one subscriber connection for each `ConnectionFactory` to the subscriber `Context`, potentially allowing for one subscriber
connection factory. `Connection` for each `ConnectionFactory`.
Application code is required to retrieve the R2DBC connection through Application code is required to retrieve the R2DBC `Connection` through
`ConnectionFactoryUtils.getConnection(ConnectionFactory)`, instead of R2DBC's standard `ConnectionFactoryUtils.getConnection(ConnectionFactory)`, instead of R2DBC's standard
`ConnectionFactory.create()`. `ConnectionFactory.create()`. All framework classes (such as `DatabaseClient`) use this
strategy implicitly. If not used with a transaction manager, the lookup strategy behaves
All framework classes (such as `DatabaseClient`) use this strategy implicitly. exactly like `ConnectionFactory.create()` and can therefore be used in any case.
If not used with this transaction manager, the lookup strategy behaves exactly like the common one.
Thus, it can be used in any case.
The `R2dbcTransactionManager` class supports custom isolation levels that get applied to the connection.

View File

@ -124,7 +124,6 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
---- ----
<1> The line that makes the bean instance transactional. <1> The line that makes the bean instance transactional.
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>` TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
tag if the bean name of the `TransactionManager` that you want to wire in has the name tag if the bean name of the `TransactionManager` that you want to wire in has the name
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject `transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
@ -522,17 +521,17 @@ The following listing shows the bean declarations:
---- ----
<tx:annotation-driven/> <tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager">
... ...
<qualifier value="order"/> <qualifier value="order"/>
</bean> </bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager">
... ...
<qualifier value="account"/> <qualifier value="account"/>
</bean> </bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager"> <bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager">
... ...
<qualifier value="reactive-account"/> <qualifier value="reactive-account"/>
</bean> </bean>

View File

@ -30,7 +30,6 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.transaction.CannotCreateTransactionException; import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.reactive.AbstractReactiveTransactionManager; import org.springframework.transaction.reactive.AbstractReactiveTransactionManager;
import org.springframework.transaction.reactive.GenericReactiveTransaction; import org.springframework.transaction.reactive.GenericReactiveTransaction;
import org.springframework.transaction.reactive.TransactionSynchronizationManager; import org.springframework.transaction.reactive.TransactionSynchronizationManager;
@ -170,7 +169,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
} }
@Override @Override
protected Object doGetTransaction(TransactionSynchronizationManager synchronizationManager) throws TransactionException { protected Object doGetTransaction(TransactionSynchronizationManager synchronizationManager) {
ConnectionFactoryTransactionObject txObject = new ConnectionFactoryTransactionObject(); ConnectionFactoryTransactionObject txObject = new ConnectionFactoryTransactionObject();
ConnectionHolder conHolder = (ConnectionHolder) synchronizationManager.getResource(obtainConnectionFactory()); ConnectionHolder conHolder = (ConnectionHolder) synchronizationManager.getResource(obtainConnectionFactory());
txObject.setConnectionHolder(conHolder, false); txObject.setConnectionHolder(conHolder, false);
@ -184,7 +183,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
@Override @Override
protected Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, Object transaction, protected Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, Object transaction,
TransactionDefinition definition) throws TransactionException { TransactionDefinition definition) {
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction; ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction;
@ -275,9 +274,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
} }
@Override @Override
protected Mono<Object> doSuspend(TransactionSynchronizationManager synchronizationManager, Object transaction) protected Mono<Object> doSuspend(TransactionSynchronizationManager synchronizationManager, Object transaction) {
throws TransactionException {
return Mono.defer(() -> { return Mono.defer(() -> {
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction; ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction;
txObject.setConnectionHolder(null); txObject.setConnectionHolder(null);
@ -287,7 +284,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
@Override @Override
protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager, protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager,
@Nullable Object transaction, Object suspendedResources) throws TransactionException { @Nullable Object transaction, Object suspendedResources) {
return Mono.defer(() -> { return Mono.defer(() -> {
synchronizationManager.bindResource(obtainConnectionFactory(), suspendedResources); synchronizationManager.bindResource(obtainConnectionFactory(), suspendedResources);
@ -297,7 +294,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
@Override @Override
protected Mono<Void> doCommit(TransactionSynchronizationManager TransactionSynchronizationManager, protected Mono<Void> doCommit(TransactionSynchronizationManager TransactionSynchronizationManager,
GenericReactiveTransaction status) throws TransactionException { GenericReactiveTransaction status) {
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction(); ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction();
if (status.isDebug()) { if (status.isDebug()) {
@ -309,7 +306,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
@Override @Override
protected Mono<Void> doRollback(TransactionSynchronizationManager TransactionSynchronizationManager, protected Mono<Void> doRollback(TransactionSynchronizationManager TransactionSynchronizationManager,
GenericReactiveTransaction status) throws TransactionException { GenericReactiveTransaction status) {
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction(); ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction();
if (status.isDebug()) { if (status.isDebug()) {
@ -321,7 +318,7 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
@Override @Override
protected Mono<Void> doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager, protected Mono<Void> doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException { GenericReactiveTransaction status) {
return Mono.fromRunnable(() -> { return Mono.fromRunnable(() -> {
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction(); ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) status.getTransaction();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,10 +31,10 @@ import org.springframework.lang.Nullable;
* template methods for specific states of the underlying transaction, * template methods for specific states of the underlying transaction,
* for example: begin, suspend, resume, commit. * for example: begin, suspend, resume, commit.
* *
* <p>The default implementations of this strategy interface are * <p>A classic implementation of this strategy interface is
* {@link org.springframework.transaction.jta.JtaTransactionManager} and * {@link org.springframework.transaction.jta.JtaTransactionManager}. However,
* {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}, * in common single-resource scenarios, Spring's specific transaction managers
* which can serve as an implementation guide for other transaction strategies. * for e.g. JDBC, JPA, JMS are preferred choices.
* *
* @author Rod Johnson * @author Rod Johnson
* @author Juergen Hoeller * @author Juergen Hoeller
@ -81,12 +81,9 @@ public interface PlatformTransactionManager extends TransactionManager {
* <p>Note that when the commit call completes, no matter if normally or * <p>Note that when the commit call completes, no matter if normally or
* throwing an exception, the transaction must be fully completed and * throwing an exception, the transaction must be fully completed and
* cleaned up. No rollback call should be expected in such a case. * cleaned up. No rollback call should be expected in such a case.
* <p>If this method throws an exception other than a TransactionException, * <p>Depending on the concrete transaction manager setup, {@code commit}
* then some before-commit error caused the commit attempt to fail. For * may propagate {@link org.springframework.dao.DataAccessException} as well,
* example, an O/R Mapping tool might have tried to flush changes to the * either from before-commit flushes or from the actual commit step.
* database right before commit, with the resulting DataAccessException
* causing the transaction to fail. The original exception will be
* propagated to the caller of this commit method in such a case.
* @param status object returned by the {@code getTransaction} method * @param status object returned by the {@code getTransaction} method
* @throws UnexpectedRollbackException in case of an unexpected rollback * @throws UnexpectedRollbackException in case of an unexpected rollback
* that the transaction coordinator initiated * that the transaction coordinator initiated
@ -110,6 +107,8 @@ public interface PlatformTransactionManager extends TransactionManager {
* The transaction will already have been completed and cleaned up when commit * The transaction will already have been completed and cleaned up when commit
* returns, even in case of a commit exception. Consequently, a rollback call * returns, even in case of a commit exception. Consequently, a rollback call
* after commit failure will lead to an IllegalTransactionStateException. * after commit failure will lead to an IllegalTransactionStateException.
* <p>Depending on the concrete transaction manager setup, {@code rollback}
* may propagate {@link org.springframework.dao.DataAccessException} as well.
* @param status object returned by the {@code getTransaction} method * @param status object returned by the {@code getTransaction} method
* @throws TransactionSystemException in case of rollback or system errors * @throws TransactionSystemException in case of rollback or system errors
* (typically caused by fundamental resource failures) * (typically caused by fundamental resource failures)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -46,6 +46,8 @@ public interface ReactiveTransactionManager extends TransactionManager {
* <p>An exception to the above rule is the read-only flag, which should be * <p>An exception to the above rule is the read-only flag, which should be
* ignored if no explicit read-only mode is supported. Essentially, the * ignored if no explicit read-only mode is supported. Essentially, the
* read-only flag is just a hint for potential optimization. * read-only flag is just a hint for potential optimization.
* <p>Note: In contrast to {@link PlatformTransactionManager}, exceptions
* are propagated through the reactive pipeline returned from this method.
* @param definition the TransactionDefinition instance, * @param definition the TransactionDefinition instance,
* describing propagation behavior, isolation level, timeout etc. * describing propagation behavior, isolation level, timeout etc.
* @return transaction status object representing the new or current transaction * @return transaction status object representing the new or current transaction
@ -58,8 +60,7 @@ public interface ReactiveTransactionManager extends TransactionManager {
* @see TransactionDefinition#getTimeout * @see TransactionDefinition#getTimeout
* @see TransactionDefinition#isReadOnly * @see TransactionDefinition#isReadOnly
*/ */
Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition) Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition);
throws TransactionException;
/** /**
* Commit the given transaction, with regard to its status. If the transaction * Commit the given transaction, with regard to its status. If the transaction
@ -69,14 +70,12 @@ public interface ReactiveTransactionManager extends TransactionManager {
* has been suspended to be able to create a new one, resume the previous * has been suspended to be able to create a new one, resume the previous
* transaction after committing the new one. * transaction after committing the new one.
* <p>Note that when the commit call completes, no matter if normally or * <p>Note that when the commit call completes, no matter if normally or
* throwing an exception, the transaction must be fully completed and * propagating an exception, the transaction must be fully completed and
* cleaned up. No rollback call should be expected in such a case. * cleaned up. No rollback call should be expected in such a case.
* <p>If this method throws an exception other than a TransactionException, * <p>Note: In contrast to {@link PlatformTransactionManager}, exceptions
* then some before-commit error caused the commit attempt to fail. For * are propagated through the reactive pipeline returned from this method.
* example, an O/R Mapping tool might have tried to flush changes to the * Also, depending on the transaction manager implementation, {@code commit}
* database right before commit, with the resulting DataAccessException * may propagate {@link org.springframework.dao.DataAccessException} as well.
* causing the transaction to fail. The original exception will be
* propagated to the caller of this commit method in such a case.
* @param transaction object returned by the {@code getTransaction} method * @param transaction object returned by the {@code getTransaction} method
* @throws UnexpectedRollbackException in case of an unexpected rollback * @throws UnexpectedRollbackException in case of an unexpected rollback
* that the transaction coordinator initiated * that the transaction coordinator initiated
@ -88,7 +87,7 @@ public interface ReactiveTransactionManager extends TransactionManager {
* is already completed (that is, committed or rolled back) * is already completed (that is, committed or rolled back)
* @see ReactiveTransaction#setRollbackOnly * @see ReactiveTransaction#setRollbackOnly
*/ */
Mono<Void> commit(ReactiveTransaction transaction) throws TransactionException; Mono<Void> commit(ReactiveTransaction transaction);
/** /**
* Perform a rollback of the given transaction. * Perform a rollback of the given transaction.
@ -96,16 +95,20 @@ public interface ReactiveTransactionManager extends TransactionManager {
* participation in the surrounding transaction. If a previous transaction * participation in the surrounding transaction. If a previous transaction
* has been suspended to be able to create a new one, resume the previous * has been suspended to be able to create a new one, resume the previous
* transaction after rolling back the new one. * transaction after rolling back the new one.
* <p><b>Do not call rollback on a transaction if commit threw an exception.</b> * <p><b>Do not call rollback on a transaction if commit failed.</b>
* The transaction will already have been completed and cleaned up when commit * The transaction will already have been completed and cleaned up when commit
* returns, even in case of a commit exception. Consequently, a rollback call * returns, even in case of a commit exception. Consequently, a rollback call
* after commit failure will lead to an IllegalTransactionStateException. * after commit failure will lead to an IllegalTransactionStateException.
* <p>Note: In contrast to {@link PlatformTransactionManager}, exceptions
* are propagated through the reactive pipeline returned from this method.
* Also, depending on the transaction manager implementation, {@code rollback}
* may propagate {@link org.springframework.dao.DataAccessException} as well.
* @param transaction object returned by the {@code getTransaction} method * @param transaction object returned by the {@code getTransaction} method
* @throws TransactionSystemException in case of rollback or system errors * @throws TransactionSystemException in case of rollback or system errors
* (typically caused by fundamental resource failures) * (typically caused by fundamental resource failures)
* @throws IllegalTransactionStateException if the given transaction * @throws IllegalTransactionStateException if the given transaction
* is already completed (that is, committed or rolled back) * is already completed (that is, committed or rolled back)
*/ */
Mono<Void> rollback(ReactiveTransaction transaction) throws TransactionException; Mono<Void> rollback(ReactiveTransaction transaction);
} }

View File

@ -95,9 +95,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @see #doBegin * @see #doBegin
*/ */
@Override @Override
public final Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition) public final Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition) {
throws TransactionException {
// Use defaults if no transaction definition given. // Use defaults if no transaction definition given.
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults()); TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
@ -165,7 +163,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* Create a ReactiveTransaction for an existing transaction. * Create a ReactiveTransaction for an existing transaction.
*/ */
private Mono<ReactiveTransaction> handleExistingTransaction(TransactionSynchronizationManager synchronizationManager, private Mono<ReactiveTransaction> handleExistingTransaction(TransactionSynchronizationManager synchronizationManager,
TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { TransactionDefinition definition, Object transaction, boolean debugEnabled) {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
return Mono.error(new IllegalTransactionStateException( return Mono.error(new IllegalTransactionStateException(
@ -277,7 +275,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @see #resume * @see #resume
*/ */
private Mono<SuspendedResourcesHolder> suspend(TransactionSynchronizationManager synchronizationManager, private Mono<SuspendedResourcesHolder> suspend(TransactionSynchronizationManager synchronizationManager,
@Nullable Object transaction) throws TransactionException { @Nullable Object transaction) {
if (synchronizationManager.isSynchronizationActive()) { if (synchronizationManager.isSynchronizationActive()) {
Mono<List<TransactionSynchronization>> suspendedSynchronizations = doSuspendSynchronization(synchronizationManager); Mono<List<TransactionSynchronization>> suspendedSynchronizations = doSuspendSynchronization(synchronizationManager);
@ -325,8 +323,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @see #suspend * @see #suspend
*/ */
private Mono<Void> resume(TransactionSynchronizationManager synchronizationManager, private Mono<Void> resume(TransactionSynchronizationManager synchronizationManager,
@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) @Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) {
throws TransactionException {
Mono<Void> resume = Mono.empty(); Mono<Void> resume = Mono.empty();
@ -403,7 +400,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @see #rollback * @see #rollback
*/ */
@Override @Override
public final Mono<Void> commit(ReactiveTransaction transaction) throws TransactionException { public final Mono<Void> commit(ReactiveTransaction transaction) {
if (transaction.isCompleted()) { if (transaction.isCompleted()) {
return Mono.error(new IllegalTransactionStateException( return Mono.error(new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction")); "Transaction is already completed - do not call commit or rollback more than once per transaction"));
@ -426,10 +423,9 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* Rollback-only flags have already been checked and applied. * Rollback-only flags have already been checked and applied.
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param status object representing the transaction * @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/ */
private Mono<Void> processCommit(TransactionSynchronizationManager synchronizationManager, private Mono<Void> processCommit(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException { GenericReactiveTransaction status) {
AtomicBoolean beforeCompletionInvoked = new AtomicBoolean(); AtomicBoolean beforeCompletionInvoked = new AtomicBoolean();
@ -489,7 +485,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @see #doSetRollbackOnly * @see #doSetRollbackOnly
*/ */
@Override @Override
public final Mono<Void> rollback(ReactiveTransaction transaction) throws TransactionException { public final Mono<Void> rollback(ReactiveTransaction transaction) {
if (transaction.isCompleted()) { if (transaction.isCompleted()) {
return Mono.error(new IllegalTransactionStateException( return Mono.error(new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction")); "Transaction is already completed - do not call commit or rollback more than once per transaction"));
@ -505,7 +501,6 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* The completed flag has already been checked. * The completed flag has already been checked.
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param status object representing the transaction * @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*/ */
private Mono<Void> processRollback(TransactionSynchronizationManager synchronizationManager, private Mono<Void> processRollback(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) { GenericReactiveTransaction status) {
@ -544,11 +539,10 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param status object representing the transaction * @param status object representing the transaction
* @param ex the thrown application exception or error * @param ex the thrown application exception or error
* @throws TransactionException in case of rollback failure
* @see #doRollback * @see #doRollback
*/ */
private Mono<Void> doRollbackOnCommitException(TransactionSynchronizationManager synchronizationManager, private Mono<Void> doRollbackOnCommitException(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status, Throwable ex) throws TransactionException { GenericReactiveTransaction status, Throwable ex) {
return Mono.defer(() -> { return Mono.defer(() -> {
if (status.isNewTransaction()) { if (status.isNewTransaction()) {
@ -716,14 +710,12 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @return the current transaction object * @return the current transaction object
* @throws org.springframework.transaction.CannotCreateTransactionException * @throws org.springframework.transaction.CannotCreateTransactionException
* if transaction support is not available * if transaction support is not available
* @throws TransactionException in case of lookup or system errors
* @see #doBegin * @see #doBegin
* @see #doCommit * @see #doCommit
* @see #doRollback * @see #doRollback
* @see GenericReactiveTransaction#getTransaction * @see GenericReactiveTransaction#getTransaction
*/ */
protected abstract Object doGetTransaction(TransactionSynchronizationManager synchronizationManager) protected abstract Object doGetTransaction(TransactionSynchronizationManager synchronizationManager);
throws TransactionException;
/** /**
* Check if the given transaction object indicates an existing transaction * Check if the given transaction object indicates an existing transaction
@ -737,10 +729,9 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* Subclasses are of course encouraged to provide such support. * Subclasses are of course encouraged to provide such support.
* @param transaction the transaction object returned by doGetTransaction * @param transaction the transaction object returned by doGetTransaction
* @return if there is an existing transaction * @return if there is an existing transaction
* @throws TransactionException in case of system errors
* @see #doGetTransaction * @see #doGetTransaction
*/ */
protected boolean isExistingTransaction(Object transaction) throws TransactionException { protected boolean isExistingTransaction(Object transaction) {
return false; return false;
} }
@ -759,12 +750,11 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @param transaction the transaction object returned by {@code doGetTransaction} * @param transaction the transaction object returned by {@code doGetTransaction}
* @param definition a TransactionDefinition instance, describing propagation * @param definition a TransactionDefinition instance, describing propagation
* behavior, isolation level, read-only flag, timeout, and transaction name * behavior, isolation level, read-only flag, timeout, and transaction name
* @throws TransactionException in case of creation or system errors
* @throws org.springframework.transaction.NestedTransactionNotSupportedException * @throws org.springframework.transaction.NestedTransactionNotSupportedException
* if the underlying transaction does not support nesting (e.g. through savepoints) * if the underlying transaction does not support nesting (e.g. through savepoints)
*/ */
protected abstract Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, protected abstract Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager,
Object transaction, TransactionDefinition definition) throws TransactionException; Object transaction, TransactionDefinition definition);
/** /**
* Suspend the resources of the current transaction. * Suspend the resources of the current transaction.
@ -777,11 +767,10 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* (will be kept unexamined for passing it into doResume) * (will be kept unexamined for passing it into doResume)
* @throws org.springframework.transaction.TransactionSuspensionNotSupportedException * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
* if suspending is not supported by the transaction manager implementation * if suspending is not supported by the transaction manager implementation
* @throws TransactionException in case of system errors
* @see #doResume * @see #doResume
*/ */
protected Mono<Object> doSuspend(TransactionSynchronizationManager synchronizationManager, protected Mono<Object> doSuspend(TransactionSynchronizationManager synchronizationManager,
Object transaction) throws TransactionException { Object transaction) {
throw new TransactionSuspensionNotSupportedException( throw new TransactionSuspensionNotSupportedException(
"Transaction manager [" + getClass().getName() + "] does not support transaction suspension"); "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
@ -798,11 +787,10 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* as returned by doSuspend * as returned by doSuspend
* @throws org.springframework.transaction.TransactionSuspensionNotSupportedException * @throws org.springframework.transaction.TransactionSuspensionNotSupportedException
* if suspending is not supported by the transaction manager implementation * if suspending is not supported by the transaction manager implementation
* @throws TransactionException in case of system errors
* @see #doSuspend * @see #doSuspend
*/ */
protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager, protected Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager,
@Nullable Object transaction, Object suspendedResources) throws TransactionException { @Nullable Object transaction, Object suspendedResources) {
throw new TransactionSuspensionNotSupportedException( throw new TransactionSuspensionNotSupportedException(
"Transaction manager [" + getClass().getName() + "] does not support transaction suspension"); "Transaction manager [" + getClass().getName() + "] does not support transaction suspension");
@ -832,11 +820,10 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* contained in the passed-in status. * contained in the passed-in status.
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param status the status representation of the transaction * @param status the status representation of the transaction
* @throws TransactionException in case of commit or system errors
* @see GenericReactiveTransaction#getTransaction * @see GenericReactiveTransaction#getTransaction
*/ */
protected abstract Mono<Void> doCommit(TransactionSynchronizationManager synchronizationManager, protected abstract Mono<Void> doCommit(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException; GenericReactiveTransaction status);
/** /**
* Perform an actual rollback of the given transaction. * Perform an actual rollback of the given transaction.
@ -845,11 +832,10 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* will be performed on the transaction object contained in the passed-in status. * will be performed on the transaction object contained in the passed-in status.
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param status the status representation of the transaction * @param status the status representation of the transaction
* @throws TransactionException in case of system errors
* @see GenericReactiveTransaction#getTransaction * @see GenericReactiveTransaction#getTransaction
*/ */
protected abstract Mono<Void> doRollback(TransactionSynchronizationManager synchronizationManager, protected abstract Mono<Void> doRollback(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException; GenericReactiveTransaction status);
/** /**
* Set the given transaction rollback-only. Only called on rollback * Set the given transaction rollback-only. Only called on rollback
@ -859,10 +845,9 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* supported. Subclasses are of course encouraged to provide such support. * supported. Subclasses are of course encouraged to provide such support.
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param status the status representation of the transaction * @param status the status representation of the transaction
* @throws TransactionException in case of system errors
*/ */
protected Mono<Void> doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager, protected Mono<Void> doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status) throws TransactionException { GenericReactiveTransaction status) {
throw new IllegalTransactionStateException( throw new IllegalTransactionStateException(
"Participating in existing transactions is not supported - when 'isExistingTransaction' " + "Participating in existing transactions is not supported - when 'isExistingTransaction' " +
@ -880,13 +865,12 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
* @param synchronizationManager the synchronization manager bound to the current transaction * @param synchronizationManager the synchronization manager bound to the current transaction
* @param transaction the transaction object returned by {@code doGetTransaction} * @param transaction the transaction object returned by {@code doGetTransaction}
* @param synchronizations a List of TransactionSynchronization objects * @param synchronizations a List of TransactionSynchronization objects
* @throws TransactionException in case of system errors
* @see #invokeAfterCompletion(TransactionSynchronizationManager, List, int) * @see #invokeAfterCompletion(TransactionSynchronizationManager, List, int)
* @see TransactionSynchronization#afterCompletion(int) * @see TransactionSynchronization#afterCompletion(int)
* @see TransactionSynchronization#STATUS_UNKNOWN * @see TransactionSynchronization#STATUS_UNKNOWN
*/ */
protected Mono<Void> registerAfterCompletionWithExistingTransaction(TransactionSynchronizationManager synchronizationManager, protected Mono<Void> registerAfterCompletionWithExistingTransaction(TransactionSynchronizationManager synchronizationManager,
Object transaction, List<TransactionSynchronization> synchronizations) throws TransactionException { Object transaction, List<TransactionSynchronization> synchronizations) {
logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " + logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " +
"processing Spring after-completion callbacks immediately, with outcome status 'unknown'"); "processing Spring after-completion callbacks immediately, with outcome status 'unknown'");