public class R2dbcTransactionManager extends AbstractReactiveTransactionManager implements InitializingBean
ReactiveTransactionManager
implementation for a single R2DBC ConnectionFactory
. This class is
capable of working in any environment with any R2DBC driver, as long as the
setup uses a ConnectionFactory
as its Connection
factory
mechanism. Binds a R2DBC Connection
from the specified
ConnectionFactory
to the current subscriber context, potentially
allowing for one context-bound Connection
per ConnectionFactory
.
Note: The ConnectionFactory
that this transaction manager
operates on needs to return independent Connection
s.
The Connection
s may come from a pool (the typical case), but the
ConnectionFactory
must not return scoped scoped Connection
s
or the like. This transaction manager will associate Connection
with context-bound transactions itself, according to the specified propagation
behavior. It assumes that a separate, independent Connection
can
be obtained even during an ongoing transaction.
Application code is required to retrieve the R2DBC Connection via
ConnectionFactoryUtils.getConnection(ConnectionFactory)
instead of a standard R2DBC-style ConnectionFactory.create()
call.
Spring classes such as DatabaseClient
use this strategy implicitly.
If not used in combination with this transaction manager, the
ConnectionFactoryUtils
lookup strategy behaves exactly like the
native ConnectionFactory
lookup; it can thus be used in a portable fashion.
Alternatively, you can allow application code to work with the standard
R2DBC lookup pattern ConnectionFactory.create()
, for example for code
that is not aware of Spring at all. In that case, define a
TransactionAwareConnectionFactoryProxy
for your target ConnectionFactory
,
and pass that proxy ConnectionFactory
to your DAOs, which will automatically
participate in Spring-managed transactions when accessing it.
This transaction manager triggers flush callbacks on registered transaction
synchronizations (if synchronization is generally active), assuming resources
operating on the underlying R2DBC Connection
.
AbstractReactiveTransactionManager.SuspendedResourcesHolder
logger
Constructor and Description |
---|
R2dbcTransactionManager()
Create a new @link ConnectionFactoryTransactionManager} instance.
|
R2dbcTransactionManager(io.r2dbc.spi.ConnectionFactory connectionFactory)
Create a new
R2dbcTransactionManager instance. |
Modifier and Type | Method and Description |
---|---|
void |
afterPropertiesSet()
Invoked by the containing
BeanFactory after it has set all bean properties
and satisfied BeanFactoryAware , ApplicationContextAware etc. |
protected Duration |
determineTimeout(TransactionDefinition definition)
Determine the actual timeout to use for the given definition.
|
protected reactor.core.publisher.Mono<Void> |
doBegin(TransactionSynchronizationManager synchronizationManager,
Object transaction,
TransactionDefinition definition)
Begin a new transaction with semantics according to the given transaction
definition.
|
protected reactor.core.publisher.Mono<Void> |
doCleanupAfterCompletion(TransactionSynchronizationManager synchronizationManager,
Object transaction)
Cleanup resources after transaction completion.
|
protected reactor.core.publisher.Mono<Void> |
doCommit(TransactionSynchronizationManager TransactionSynchronizationManager,
GenericReactiveTransaction status)
Perform an actual commit of the given transaction.
|
protected Object |
doGetTransaction(TransactionSynchronizationManager synchronizationManager)
Return a transaction object for the current transaction state.
|
protected reactor.core.publisher.Mono<Void> |
doResume(TransactionSynchronizationManager synchronizationManager,
Object transaction,
Object suspendedResources)
Resume the resources of the current transaction.
|
protected reactor.core.publisher.Mono<Void> |
doRollback(TransactionSynchronizationManager TransactionSynchronizationManager,
GenericReactiveTransaction status)
Perform an actual rollback of the given transaction.
|
protected reactor.core.publisher.Mono<Void> |
doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager,
GenericReactiveTransaction status)
Set the given transaction rollback-only.
|
protected reactor.core.publisher.Mono<Object> |
doSuspend(TransactionSynchronizationManager synchronizationManager,
Object transaction)
Suspend the resources of the current transaction.
|
io.r2dbc.spi.ConnectionFactory |
getConnectionFactory()
Return the R2DBC
ConnectionFactory that this instance manages transactions for. |
boolean |
isEnforceReadOnly()
Return whether to enforce the read-only nature of a transaction through an
explicit statement on the transactional connection.
|
protected boolean |
isExistingTransaction(Object transaction)
Check if the given transaction object indicates an existing transaction
(that is, a transaction which has already started).
|
protected io.r2dbc.spi.ConnectionFactory |
obtainConnectionFactory()
Obtain the
ConnectionFactory for actual use. |
protected reactor.core.publisher.Mono<Void> |
prepareTransactionalConnection(io.r2dbc.spi.Connection con,
TransactionDefinition definition,
Object transaction)
Prepare the transactional
Connection right after transaction begin. |
protected io.r2dbc.spi.IsolationLevel |
resolveIsolationLevel(int isolationLevel)
Resolve the
isolation level constant to a R2DBC
IsolationLevel . |
void |
setConnectionFactory(io.r2dbc.spi.ConnectionFactory connectionFactory)
Set the R2DBC
ConnectionFactory that this instance should manage transactions for. |
void |
setEnforceReadOnly(boolean enforceReadOnly)
Specify whether to enforce the read-only nature of a transaction (as indicated by
TransactionDefinition.isReadOnly() through an explicit statement on the
transactional connection: "SET TRANSACTION READ ONLY" as understood by Oracle,
MySQL and Postgres. |
protected RuntimeException |
translateException(String task,
io.r2dbc.spi.R2dbcException ex)
Translate the given R2DBC commit/rollback exception to a common Spring exception to propagate
from the
AbstractReactiveTransactionManager.commit(org.springframework.transaction.ReactiveTransaction) /AbstractReactiveTransactionManager.rollback(org.springframework.transaction.ReactiveTransaction) call. |
commit, getReactiveTransaction, prepareForCommit, registerAfterCompletionWithExistingTransaction, rollback
public R2dbcTransactionManager()
public R2dbcTransactionManager(io.r2dbc.spi.ConnectionFactory connectionFactory)
R2dbcTransactionManager
instance.connectionFactory
- the R2DBC ConnectionFactory to manage transactions forpublic void setConnectionFactory(@Nullable io.r2dbc.spi.ConnectionFactory connectionFactory)
ConnectionFactory
that this instance should manage transactions for.
This will typically be a locally defined ConnectionFactory
, for example an connection pool.
The ConnectionFactory
passed in here needs to return independent Connection
s.
The Connection
s may come from a pool (the typical case), but the ConnectionFactory
must not return scoped Connection
s or the like.
TransactionAwareConnectionFactoryProxy
@Nullable public io.r2dbc.spi.ConnectionFactory getConnectionFactory()
ConnectionFactory
that this instance manages transactions for.protected io.r2dbc.spi.ConnectionFactory obtainConnectionFactory()
ConnectionFactory
for actual use.ConnectionFactory
(never null
)IllegalStateException
- in case of no ConnectionFactory setpublic void setEnforceReadOnly(boolean enforceReadOnly)
TransactionDefinition.isReadOnly()
through an explicit statement on the
transactional connection: "SET TRANSACTION READ ONLY" as understood by Oracle,
MySQL and Postgres.
The exact treatment, including any SQL statement executed on the connection,
can be customized through through prepareTransactionalConnection(io.r2dbc.spi.Connection, org.springframework.transaction.TransactionDefinition, java.lang.Object)
.
public boolean isEnforceReadOnly()
setEnforceReadOnly(boolean)
public void afterPropertiesSet()
InitializingBean
BeanFactory
after it has set all bean properties
and satisfied BeanFactoryAware
, ApplicationContextAware
etc.
This method allows the bean instance to perform validation of its overall configuration and final initialization when all bean properties have been set.
afterPropertiesSet
in interface InitializingBean
protected Object doGetTransaction(TransactionSynchronizationManager synchronizationManager) throws TransactionException
AbstractReactiveTransactionManager
The returned object will usually be specific to the concrete transaction manager implementation, carrying corresponding transaction state in a modifiable fashion. This object will be passed into the other template methods (e.g. doBegin and doCommit), either directly or as part of a DefaultReactiveTransactionStatus instance.
The returned object should contain information about any existing
transaction, that is, a transaction that has already started before the
current getTransaction
call on the transaction manager.
Consequently, a doGetTransaction
implementation will usually
look for an existing transaction and store corresponding state in the
returned transaction object.
doGetTransaction
in class AbstractReactiveTransactionManager
synchronizationManager
- the synchronization manager bound to the current transactionCannotCreateTransactionException
- if transaction support is not availableTransactionException
- in case of lookup or system errorsAbstractReactiveTransactionManager.doBegin(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object, org.springframework.transaction.TransactionDefinition)
,
AbstractReactiveTransactionManager.doCommit(org.springframework.transaction.reactive.TransactionSynchronizationManager, org.springframework.transaction.reactive.GenericReactiveTransaction)
,
AbstractReactiveTransactionManager.doRollback(org.springframework.transaction.reactive.TransactionSynchronizationManager, org.springframework.transaction.reactive.GenericReactiveTransaction)
,
GenericReactiveTransaction.getTransaction()
protected boolean isExistingTransaction(Object transaction)
AbstractReactiveTransactionManager
The result will be evaluated according to the specified propagation behavior for the new transaction. An existing transaction might get suspended (in case of PROPAGATION_REQUIRES_NEW), or the new transaction might participate in the existing one (in case of PROPAGATION_REQUIRED).
The default implementation returns false
, assuming that
participating in existing transactions is generally not supported.
Subclasses are of course encouraged to provide such support.
isExistingTransaction
in class AbstractReactiveTransactionManager
transaction
- the transaction object returned by doGetTransactionAbstractReactiveTransactionManager.doGetTransaction(org.springframework.transaction.reactive.TransactionSynchronizationManager)
protected reactor.core.publisher.Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, Object transaction, TransactionDefinition definition) throws TransactionException
AbstractReactiveTransactionManager
This method gets called when the transaction manager has decided to actually start a new transaction. Either there wasn't any transaction before, or the previous transaction has been suspended.
A special scenario is a nested transaction: This method will be called to start a nested transaction when necessary. In such a context, there will be an active transaction: The implementation of this method has to detect this and start an appropriate nested transaction.
doBegin
in class AbstractReactiveTransactionManager
synchronizationManager
- the synchronization manager bound to the new transactiontransaction
- the transaction object returned by doGetTransaction
definition
- a TransactionDefinition instance, describing propagation
behavior, isolation level, read-only flag, timeout, and transaction nameTransactionException
- in case of creation or system errorsNestedTransactionNotSupportedException
- if the underlying transaction does not support nesting (e.g. through savepoints)protected Duration determineTimeout(TransactionDefinition definition)
definition
- the transaction definitionTransactionDefinition.getTimeout()
protected reactor.core.publisher.Mono<Object> doSuspend(TransactionSynchronizationManager synchronizationManager, Object transaction) throws TransactionException
AbstractReactiveTransactionManager
The default implementation throws a TransactionSuspensionNotSupportedException, assuming that transaction suspension is generally not supported.
doSuspend
in class AbstractReactiveTransactionManager
synchronizationManager
- the synchronization manager bound to the current transactiontransaction
- the transaction object returned by doGetTransaction
TransactionSuspensionNotSupportedException
- if suspending is not supported by the transaction manager implementationTransactionException
- in case of system errorsAbstractReactiveTransactionManager.doResume(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object, java.lang.Object)
protected reactor.core.publisher.Mono<Void> doResume(TransactionSynchronizationManager synchronizationManager, @Nullable Object transaction, Object suspendedResources) throws TransactionException
AbstractReactiveTransactionManager
The default implementation throws a TransactionSuspensionNotSupportedException, assuming that transaction suspension is generally not supported.
doResume
in class AbstractReactiveTransactionManager
synchronizationManager
- the synchronization manager bound to the current transactiontransaction
- the transaction object returned by doGetTransaction
suspendedResources
- the object that holds suspended resources,
as returned by doSuspendTransactionSuspensionNotSupportedException
- if suspending is not supported by the transaction manager implementationTransactionException
- in case of system errorsAbstractReactiveTransactionManager.doSuspend(org.springframework.transaction.reactive.TransactionSynchronizationManager, java.lang.Object)
protected reactor.core.publisher.Mono<Void> doCommit(TransactionSynchronizationManager TransactionSynchronizationManager, GenericReactiveTransaction status) throws TransactionException
AbstractReactiveTransactionManager
An implementation does not need to check the "new transaction" flag or the rollback-only flag; this will already have been handled before. Usually, a straight commit will be performed on the transaction object contained in the passed-in status.
doCommit
in class AbstractReactiveTransactionManager
TransactionSynchronizationManager
- the synchronization manager bound to the current transactionstatus
- the status representation of the transactionTransactionException
- in case of commit or system errorsGenericReactiveTransaction.getTransaction()
protected reactor.core.publisher.Mono<Void> doRollback(TransactionSynchronizationManager TransactionSynchronizationManager, GenericReactiveTransaction status) throws TransactionException
AbstractReactiveTransactionManager
An implementation does not need to check the "new transaction" flag; this will already have been handled before. Usually, a straight rollback will be performed on the transaction object contained in the passed-in status.
doRollback
in class AbstractReactiveTransactionManager
TransactionSynchronizationManager
- the synchronization manager bound to the current transactionstatus
- the status representation of the transactionTransactionException
- in case of system errorsGenericReactiveTransaction.getTransaction()
protected reactor.core.publisher.Mono<Void> doSetRollbackOnly(TransactionSynchronizationManager synchronizationManager, GenericReactiveTransaction status) throws TransactionException
AbstractReactiveTransactionManager
The default implementation throws an IllegalTransactionStateException, assuming that participating in existing transactions is generally not supported. Subclasses are of course encouraged to provide such support.
doSetRollbackOnly
in class AbstractReactiveTransactionManager
synchronizationManager
- the synchronization manager bound to the current transactionstatus
- the status representation of the transactionTransactionException
- in case of system errorsprotected reactor.core.publisher.Mono<Void> doCleanupAfterCompletion(TransactionSynchronizationManager synchronizationManager, Object transaction)
AbstractReactiveTransactionManager
Called after doCommit
and doRollback
execution,
on any outcome. The default implementation does nothing.
Should not throw any exceptions but just issue warnings on errors.
doCleanupAfterCompletion
in class AbstractReactiveTransactionManager
synchronizationManager
- the synchronization manager bound to the current transactiontransaction
- the transaction object returned by doGetTransaction
protected reactor.core.publisher.Mono<Void> prepareTransactionalConnection(io.r2dbc.spi.Connection con, TransactionDefinition definition, Object transaction)
Connection
right after transaction begin.
The default implementation executes a "SET TRANSACTION READ ONLY" statement if the
"enforceReadOnly"
flag is set to true
and the
transaction definition indicates a read-only transaction.
The "SET TRANSACTION READ ONLY" is understood by Oracle, MySQL and Postgres and may work with other databases as well. If you'd like to adapt this treatment, override this method accordingly.
con
- the transactional R2DBC Connectiondefinition
- the current transaction definitiontransaction
- the transaction objectsetEnforceReadOnly(boolean)
@Nullable protected io.r2dbc.spi.IsolationLevel resolveIsolationLevel(int isolationLevel)
isolation level constant
to a R2DBC
IsolationLevel
. If you'd like to extend isolation level translation for vendor-specific
IsolationLevel
s, override this method accordingly.isolationLevel
- the isolation level to translate.null
if not resolvable or the isolation level
should remain default
.TransactionDefinition.getIsolationLevel()
protected RuntimeException translateException(String task, io.r2dbc.spi.R2dbcException ex)
AbstractReactiveTransactionManager.commit(org.springframework.transaction.ReactiveTransaction)
/AbstractReactiveTransactionManager.rollback(org.springframework.transaction.ReactiveTransaction)
call.task
- the task description (commit or rollback).ex
- the SQLException thrown from commit/rollback.