Merge branch '5.2.x'
This commit is contained in:
commit
1e64ffa7ba
|
@ -150,14 +150,18 @@ configuration file need to change (rather than your code).
|
||||||
[[transaction-strategies]]
|
[[transaction-strategies]]
|
||||||
=== Understanding the Spring Framework Transaction Abstraction
|
=== Understanding the Spring Framework Transaction Abstraction
|
||||||
|
|
||||||
The key to the Spring transaction abstraction is the notion of a transaction
|
The key to the Spring transaction abstraction is the notion of a transaction strategy. A
|
||||||
strategy. A transaction strategy is defined by the
|
transaction strategy is defined by a `TransactionManager`, specifically the
|
||||||
`org.springframework.transaction.PlatformTransactionManager` interface, which the following listing shows:
|
`org.springframework.transaction.PlatformTransactionManager` interface for imperative
|
||||||
|
transaction management and the
|
||||||
|
`org.springframework.transaction.ReactiveTransactionManager` interface for reactive
|
||||||
|
transaction management. The following listing shows the definition of the
|
||||||
|
`PlatformTransactionManager` API:
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
.Java
|
.Java
|
||||||
----
|
----
|
||||||
public interface PlatformTransactionManager {
|
public interface PlatformTransactionManager extends TransactionManager {
|
||||||
|
|
||||||
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
|
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
|
||||||
|
|
||||||
|
@ -169,7 +173,7 @@ strategy. A transaction strategy is defined by the
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
.Kotlin
|
.Kotlin
|
||||||
----
|
----
|
||||||
interface PlatformTransactionManager {
|
interface PlatformTransactionManager : TransactionManager {
|
||||||
|
|
||||||
@Throws(TransactionException::class)
|
@Throws(TransactionException::class)
|
||||||
fun getTransaction(definition: TransactionDefinition): TransactionStatus
|
fun getTransaction(definition: TransactionDefinition): TransactionStatus
|
||||||
|
@ -188,8 +192,8 @@ This is primarily a service provider interface (SPI), although you can use it
|
||||||
necessary. It is not tied to a lookup strategy, such as JNDI.
|
necessary. It is not tied to a lookup strategy, such as JNDI.
|
||||||
`PlatformTransactionManager` implementations are defined like any other object (or bean)
|
`PlatformTransactionManager` implementations are defined like any other object (or bean)
|
||||||
in the Spring Framework IoC container. This benefit alone makes Spring Framework
|
in the Spring Framework IoC container. This benefit alone makes Spring Framework
|
||||||
transactions a worthwhile abstraction, even when you work with JTA. You can test transactional code
|
transactions a worthwhile abstraction, even when you work with JTA. You can test
|
||||||
much more easily than if it used JTA directly.
|
transactional code much more easily than if it used JTA directly.
|
||||||
|
|
||||||
Again, in keeping with Spring's philosophy, the `TransactionException` that can be thrown
|
Again, in keeping with Spring's philosophy, the `TransactionException` that can be thrown
|
||||||
by any of the `PlatformTransactionManager` interface's methods is unchecked (that
|
by any of the `PlatformTransactionManager` interface's methods is unchecked (that
|
||||||
|
@ -206,6 +210,44 @@ exists in the current call stack. The implication in this latter case is that, a
|
||||||
Java EE transaction contexts, a `TransactionStatus` is associated with a thread of
|
Java EE transaction contexts, a `TransactionStatus` is associated with a thread of
|
||||||
execution.
|
execution.
|
||||||
|
|
||||||
|
As of Spring Framework 5.2, Spring also provides a transaction management abstraction for
|
||||||
|
reactive applications that make use of reactive types or Kotlin Coroutines. The following
|
||||||
|
listing shows the transaction strategy defined by
|
||||||
|
`org.springframework.transaction.ReactiveTransactionManager`:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
public interface ReactiveTransactionManager extends TransactionManager {
|
||||||
|
|
||||||
|
Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;
|
||||||
|
|
||||||
|
Mono<Void> commit(ReactiveTransaction status) throws TransactionException;
|
||||||
|
|
||||||
|
Mono<Void> rollback(ReactiveTransaction status) throws TransactionException;
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
interface ReactiveTransactionManager : TransactionManager {
|
||||||
|
|
||||||
|
@Throws(TransactionException::class)
|
||||||
|
fun getReactiveTransaction(definition: TransactionDefinition): Mono<ReactiveTransaction>
|
||||||
|
|
||||||
|
@Throws(TransactionException::class)
|
||||||
|
fun commit(status: ReactiveTransaction): Mono<Void>
|
||||||
|
|
||||||
|
@Throws(TransactionException::class)
|
||||||
|
fun rollback(status: ReactiveTransaction): Mono<Void>
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
The reactive transaction manager is primarily a service provider interface (SPI),
|
||||||
|
although you can use it <<transaction-programmatic-rtm, programmatically>> from your
|
||||||
|
application code. Because `ReactiveTransactionManager` is an interface, it can be easily
|
||||||
|
mocked or stubbed as necessary.
|
||||||
|
|
||||||
The `TransactionDefinition` interface specifies:
|
The `TransactionDefinition` interface specifies:
|
||||||
|
|
||||||
* Propagation: Typically, all code executed within a transaction scope runs in
|
* Propagation: Typically, all code executed within a transaction scope runs in
|
||||||
|
@ -237,48 +279,52 @@ familiar, as they are common to all transaction APIs. The following listing show
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
.Java
|
.Java
|
||||||
----
|
----
|
||||||
public interface TransactionStatus extends SavepointManager {
|
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
|
||||||
|
|
||||||
|
@Override
|
||||||
boolean isNewTransaction();
|
boolean isNewTransaction();
|
||||||
|
|
||||||
boolean hasSavepoint();
|
boolean hasSavepoint();
|
||||||
|
|
||||||
|
@Override
|
||||||
void setRollbackOnly();
|
void setRollbackOnly();
|
||||||
|
|
||||||
|
@Override
|
||||||
boolean isRollbackOnly();
|
boolean isRollbackOnly();
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
|
@Override
|
||||||
boolean isCompleted();
|
boolean isCompleted();
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
.Kotlin
|
.Kotlin
|
||||||
----
|
----
|
||||||
interface TransactionStatus : SavepointManager {
|
interface TransactionStatus : TransactionExecution, SavepointManager, Flushable {
|
||||||
|
|
||||||
fun isNewTransaction(): Boolean
|
override fun isNewTransaction(): Boolean
|
||||||
|
|
||||||
fun hasSavepoint(): Boolean
|
fun hasSavepoint(): Boolean
|
||||||
|
|
||||||
fun setRollbackOnly()
|
override fun setRollbackOnly()
|
||||||
|
|
||||||
fun isRollbackOnly(): Boolean
|
override fun isRollbackOnly(): Boolean
|
||||||
|
|
||||||
fun flush()
|
fun flush()
|
||||||
|
|
||||||
fun isCompleted(): Boolean
|
override fun isCompleted(): Boolean
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Regardless of whether you opt for declarative or programmatic transaction management in
|
Regardless of whether you opt for declarative or programmatic transaction management in
|
||||||
Spring, defining the correct `PlatformTransactionManager` implementation is absolutely
|
Spring, defining the correct `TransactionManager` implementation is absolutely essential.
|
||||||
essential. You typically define this implementation through dependency injection.
|
You typically define this implementation through dependency injection.
|
||||||
|
|
||||||
`PlatformTransactionManager` implementations normally require knowledge of the
|
`TransactionManager` implementations normally require knowledge of the environment in
|
||||||
environment in which they work: JDBC, JTA, Hibernate, and so on. The following examples
|
which they work: JDBC, JTA, Hibernate, and so on. The following examples show how you can
|
||||||
show how you can define a local `PlatformTransactionManager` implementation (in this case,
|
define a local `PlatformTransactionManager` implementation (in this case, with plain
|
||||||
with plain JDBC.)
|
JDBC.)
|
||||||
|
|
||||||
You can define a JDBC `DataSource` by creating a bean similar to the following:
|
You can define a JDBC `DataSource` by creating a bean similar to the following:
|
||||||
|
|
||||||
|
@ -292,8 +338,8 @@ You can define a JDBC `DataSource` by creating a bean similar to the following:
|
||||||
</bean>
|
</bean>
|
||||||
----
|
----
|
||||||
|
|
||||||
The related `PlatformTransactionManager` bean definition then has a reference to
|
The related `PlatformTransactionManager` bean definition then has a reference to the
|
||||||
the `DataSource` definition. It should resemble the following example:
|
`DataSource` definition. It should resemble the following example:
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -303,8 +349,8 @@ the `DataSource` definition. It should resemble the following example:
|
||||||
----
|
----
|
||||||
|
|
||||||
If you use JTA in a Java EE container, then you use a container `DataSource`, obtained
|
If you use JTA in a Java EE container, then you use a container `DataSource`, obtained
|
||||||
through JNDI, in conjunction with Spring's `JtaTransactionManager`. The following example shows what the JTA
|
through JNDI, in conjunction with Spring's `JtaTransactionManager`. The following example
|
||||||
and JNDI lookup version would look like:
|
shows what the JTA and JNDI lookup version would look like:
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -335,21 +381,21 @@ NOTE: The preceding definition of the `dataSource` bean uses the `<jndi-lookup/>
|
||||||
from the `jee` namespace. For more information see
|
from the `jee` namespace. For more information see
|
||||||
<<integration.adoc#xsd-schemas-jee, The JEE Schema>>.
|
<<integration.adoc#xsd-schemas-jee, The JEE Schema>>.
|
||||||
|
|
||||||
You can also use easily Hibernate local transactions, as shown in the following
|
You can also easily use Hibernate local transactions, as shown in the following examples.
|
||||||
examples. In this case, you need to define a Hibernate `LocalSessionFactoryBean`,
|
In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your
|
||||||
which your application code can use to obtain Hibernate `Session` instances.
|
application code can use to obtain Hibernate `Session` instances.
|
||||||
|
|
||||||
The `DataSource` bean definition is similar to the local JDBC example shown
|
The `DataSource` bean definition is similar to the local JDBC example shown previously
|
||||||
previously and, thus, is not shown in the following example.
|
and, thus, is not shown in the following example.
|
||||||
|
|
||||||
NOTE: If the `DataSource` (used by any non-JTA transaction manager) is looked up through
|
NOTE: If the `DataSource` (used by any non-JTA transaction manager) is looked up through
|
||||||
JNDI and managed by a Java EE container, it should be non-transactional, because the
|
JNDI and managed by a Java EE container, it should be non-transactional, because the
|
||||||
Spring Framework (rather than the Java EE container) manages the transactions.
|
Spring Framework (rather than the Java EE container) manages the transactions.
|
||||||
|
|
||||||
The `txManager` bean in this case is of the `HibernateTransactionManager` type. In the
|
The `txManager` bean in this case is of the `HibernateTransactionManager` type. In the
|
||||||
same way as the `DataSourceTransactionManager` needs a reference to the `DataSource`,
|
same way as the `DataSourceTransactionManager` needs a reference to the `DataSource`, the
|
||||||
the `HibernateTransactionManager` needs a reference to the `SessionFactory`.
|
`HibernateTransactionManager` needs a reference to the `SessionFactory`. The following
|
||||||
The following example declares `sessionFactory` and `txManager` beans:
|
example declares `sessionFactory` and `txManager` beans:
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -372,9 +418,9 @@ The following example declares `sessionFactory` and `txManager` beans:
|
||||||
</bean>
|
</bean>
|
||||||
----
|
----
|
||||||
|
|
||||||
If you use Hibernate and Java EE container-managed JTA transactions, you
|
If you use Hibernate and Java EE container-managed JTA transactions, you should use the
|
||||||
should use the same `JtaTransactionManager` as in the previous JTA example for
|
same `JtaTransactionManager` as in the previous JTA example for JDBC, as the following
|
||||||
JDBC, as the following example shows:
|
example shows:
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -402,7 +448,7 @@ and so forth) should now be clear. This section describes how the application co
|
||||||
(directly or indirectly, by using a persistence API such as JDBC, Hibernate, or JPA)
|
(directly or indirectly, by using a persistence API such as JDBC, Hibernate, or JPA)
|
||||||
ensures that these resources are created, reused, and cleaned up properly. The section
|
ensures that these resources are created, reused, and cleaned up properly. The section
|
||||||
also discusses how transaction synchronization is (optionally) triggered through the
|
also discusses how transaction synchronization is (optionally) triggered through the
|
||||||
relevant `PlatformTransactionManager`.
|
relevant `TransactionManager`.
|
||||||
|
|
||||||
|
|
||||||
[[tx-resource-synchronization-high]]
|
[[tx-resource-synchronization-high]]
|
||||||
|
@ -416,7 +462,7 @@ synchronization of the resources, and exception mapping. Thus, user data access
|
||||||
not have to address these tasks but can focus purely on non-boilerplate
|
not have to address these tasks but can focus purely on non-boilerplate
|
||||||
persistence logic. Generally, you use the native ORM API or take a template approach
|
persistence logic. Generally, you use the native ORM API or take a template approach
|
||||||
for JDBC access by using the `JdbcTemplate`. These solutions are detailed in subsequent
|
for JDBC access by using the `JdbcTemplate`. These solutions are detailed in subsequent
|
||||||
chapters of this reference documentation.
|
sections of this reference documentation.
|
||||||
|
|
||||||
|
|
||||||
[[tx-resource-synchronization-low]]
|
[[tx-resource-synchronization-low]]
|
||||||
|
@ -533,21 +579,32 @@ on unchecked exceptions), it is often useful to customize this behavior.
|
||||||
|
|
||||||
It is not sufficient merely to tell you to annotate your classes with the
|
It is not sufficient merely to tell you to annotate your classes with the
|
||||||
`@Transactional` annotation, add `@EnableTransactionManagement` to your configuration,
|
`@Transactional` annotation, add `@EnableTransactionManagement` to your configuration,
|
||||||
and expect you to understand how it all works. To provide a deeper understanding,
|
and expect you to understand how it all works. To provide a deeper understanding, this
|
||||||
this section explains the inner workings of the Spring Framework's declarative
|
section explains the inner workings of the Spring Framework's declarative transaction
|
||||||
transaction infrastructure in the event of transaction-related issues.
|
infrastructure in the context of transaction-related issues.
|
||||||
|
|
||||||
The most important concepts to grasp with regard to the Spring Framework's declarative
|
The most important concepts to grasp with regard to the Spring Framework's declarative
|
||||||
transaction support are that this support is enabled
|
transaction support are that this support is enabled
|
||||||
<<core.adoc#aop-understanding-aop-proxies, via AOP proxies>> and that the transactional
|
<<core.adoc#aop-understanding-aop-proxies, via AOP proxies>> and that the transactional
|
||||||
advice is driven by metadata (currently XML- or annotation-based). The combination of
|
advice is driven by metadata (currently XML- or annotation-based). The combination of AOP
|
||||||
AOP with transactional metadata yields an AOP proxy that uses a `TransactionInterceptor`
|
with transactional metadata yields an AOP proxy that uses a `TransactionInterceptor` in
|
||||||
in conjunction with an appropriate `PlatformTransactionManager` implementation to drive
|
conjunction with an appropriate `TransactionManager` implementation to drive transactions
|
||||||
transactions around method invocations.
|
around method invocations.
|
||||||
|
|
||||||
NOTE: Spring AOP is covered in <<core.adoc#aop, the AOP section>>.
|
NOTE: Spring AOP is covered in <<core.adoc#aop, the AOP section>>.
|
||||||
|
|
||||||
The following images shows a Conceptual view of calling a method on a transactional proxy:
|
Spring Frameworks's `TransactionInterceptor` provides transaction management for
|
||||||
|
imperative and reactive programming models. The interceptor detects the desired flavor of
|
||||||
|
transaction management by inspecting the method return type. Methods returning a reactive
|
||||||
|
type such as `Publisher` or Kotlin `Flow` (or a subtype of those) qualify for reactive
|
||||||
|
transaction management. All other return types including `void` use the code path for
|
||||||
|
imperative transaction management.
|
||||||
|
|
||||||
|
Transaction management flavors impact which transaction manager is required. Imperative
|
||||||
|
transactions require a `PlatformTransactionManager`, while reactive transactions use
|
||||||
|
`ReactiveTransactionManager` implementations.
|
||||||
|
|
||||||
|
The following image shows a conceptual view of calling a method on a transactional proxy:
|
||||||
|
|
||||||
image::images/tx.png[]
|
image::images/tx.png[]
|
||||||
|
|
||||||
|
@ -560,8 +617,9 @@ Consider the following interface and its attendant implementation. This example
|
||||||
usage without focusing on a particular domain model. For the purposes of this example,
|
usage without focusing on a particular domain model. For the purposes of this example,
|
||||||
the fact that the `DefaultFooService` class throws `UnsupportedOperationException`
|
the fact that the `DefaultFooService` class throws `UnsupportedOperationException`
|
||||||
instances in the body of each implemented method is good. That behavior lets you see
|
instances in the body of each implemented method is good. That behavior lets you see
|
||||||
transactions be created and then rolled back in response to the
|
transactions being created and then rolled back in response to the
|
||||||
`UnsupportedOperationException` instance. The following listing shows the `FooService` interface:
|
`UnsupportedOperationException` instance. The following listing shows the `FooService`
|
||||||
|
interface:
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
.Java
|
.Java
|
||||||
|
@ -707,7 +765,7 @@ configuration is explained in detail in the next few paragraphs:
|
||||||
<property name="password" value="tiger"/>
|
<property name="password" value="tiger"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<!-- similarly, don't forget the PlatformTransactionManager -->
|
<!-- similarly, don't forget the TransactionManager -->
|
||||||
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||||
<property name="dataSource" ref="dataSource"/>
|
<property name="dataSource" ref="dataSource"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
@ -717,18 +775,18 @@ configuration is explained in detail in the next few paragraphs:
|
||||||
</beans>
|
</beans>
|
||||||
----
|
----
|
||||||
|
|
||||||
Examine the preceding configuration. It assumes that you want to make a service object, the `fooService`
|
Examine the preceding configuration. It assumes that you want to make a service object,
|
||||||
bean, transactional. The transaction semantics to apply are encapsulated in the
|
the `fooService` bean, transactional. The transaction semantics to apply are encapsulated
|
||||||
`<tx:advice/>` definition. The `<tx:advice/>` definition reads as "`all methods, on
|
in the `<tx:advice/>` definition. The `<tx:advice/>` definition reads as "all methods
|
||||||
starting with `get`, are to execute in the context of a read-only transaction, and all
|
starting with `get` are to execute in the context of a read-only transaction, and all
|
||||||
other methods are to execute with the default transaction semantics`". The
|
other methods are to execute with the default transaction semantics". The
|
||||||
`transaction-manager` attribute of the `<tx:advice/>` tag is set to the name of the
|
`transaction-manager` attribute of the `<tx:advice/>` tag is set to the name of the
|
||||||
`PlatformTransactionManager` bean that is going to drive the transactions (in this
|
`TransactionManager` bean that is going to drive the transactions (in this case, the
|
||||||
case, the `txManager` bean).
|
`txManager` bean).
|
||||||
|
|
||||||
TIP: You can omit the `transaction-manager` attribute in the transactional advice
|
TIP: You can omit the `transaction-manager` attribute in the transactional advice
|
||||||
(`<tx:advice/>`) if the bean name of the `PlatformTransactionManager` that you want to
|
(`<tx:advice/>`) if the bean name of the `TransactionManager` that you want to
|
||||||
wire in has the name `transactionManager`. If the `PlatformTransactionManager` bean that
|
wire in has the name `transactionManager`. If the `TransactionManager` bean that
|
||||||
you want to wire in has any other name, you must use the `transaction-manager`
|
you want to wire in has any other name, you must use the `transaction-manager`
|
||||||
attribute explicitly, as in the preceding example.
|
attribute explicitly, as in the preceding example.
|
||||||
|
|
||||||
|
@ -759,7 +817,7 @@ NOTE: In the preceding example, it is assumed that all your service interfaces a
|
||||||
in the `x.y.service` package. See <<core.adoc#aop, the AOP section>> for more details.
|
in the `x.y.service` package. See <<core.adoc#aop, the AOP section>> for more details.
|
||||||
|
|
||||||
Now that we have analyzed the configuration, you may be asking yourself,
|
Now that we have analyzed the configuration, you may be asking yourself,
|
||||||
"`What does all this configuration actually do?`"
|
"What does all this configuration actually do?"
|
||||||
|
|
||||||
The configuration shown earlier is used to create a transactional proxy around the object
|
The configuration shown earlier is used to create a transactional proxy around the object
|
||||||
that is created from the `fooService` bean definition. The proxy is configured with
|
that is created from the `fooService` bean definition. The proxy is configured with
|
||||||
|
@ -793,8 +851,8 @@ that test drives the configuration shown earlier:
|
||||||
----
|
----
|
||||||
|
|
||||||
The output from running the preceding program should resemble the following (the Log4J
|
The output from running the preceding program should resemble the following (the Log4J
|
||||||
output and the stack trace from the UnsupportedOperationException thrown by the
|
output and the stack trace from the `UnsupportedOperationException` thrown by the
|
||||||
insertFoo(..) method of the DefaultFooService class have been truncated for clarity):
|
`insertFoo(..)` method of the `DefaultFooService` class have been truncated for clarity):
|
||||||
|
|
||||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -826,6 +884,129 @@ insertFoo(..) method of the DefaultFooService class have been truncated for clar
|
||||||
at Boot.main(Boot.java:11)
|
at Boot.main(Boot.java:11)
|
||||||
----
|
----
|
||||||
|
|
||||||
|
To use reactive transaction management the code has to use reactive types.
|
||||||
|
|
||||||
|
NOTE: Spring Framework uses the `ReactiveAdapterRegistry` to determine whether a method
|
||||||
|
return type is reactive.
|
||||||
|
|
||||||
|
The following listing shows a modified version of the previously used `FooService`, but
|
||||||
|
this time the code uses reactive types:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
// the reactive service interface that we want to make transactional
|
||||||
|
|
||||||
|
package x.y.service;
|
||||||
|
|
||||||
|
public interface FooService {
|
||||||
|
|
||||||
|
Flux<Foo> getFoo(String fooName);
|
||||||
|
|
||||||
|
Publisher<Foo> getFoo(String fooName, String barName);
|
||||||
|
|
||||||
|
Mono<Void> insertFoo(Foo foo);
|
||||||
|
|
||||||
|
Mono<Void> updateFoo(Foo foo);
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
// the reactive service interface that we want to make transactional
|
||||||
|
|
||||||
|
package x.y.service
|
||||||
|
|
||||||
|
interface FooService {
|
||||||
|
|
||||||
|
fun getFoo(fooName: String): Flow<Foo>
|
||||||
|
|
||||||
|
fun getFoo(fooName: String, barName: String): Publisher<Foo>
|
||||||
|
|
||||||
|
fun insertFoo(foo: Foo) : Mono<Void>
|
||||||
|
|
||||||
|
fun updateFoo(foo: Foo) : Mono<Void>
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
The following example shows an implementation of the preceding interface:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
package x.y.service;
|
||||||
|
|
||||||
|
public class DefaultFooService implements FooService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<Foo> getFoo(String fooName) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Publisher<Foo> getFoo(String fooName, String barName) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> insertFoo(Foo foo) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> updateFoo(Foo foo) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
package x.y.service
|
||||||
|
|
||||||
|
class DefaultFooService : FooService {
|
||||||
|
|
||||||
|
override fun getFoo(fooName: String): Flow<Foo> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFoo(fooName: String, barName: String): Publisher<Foo> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun insertFoo(foo: Foo): Mono<Void> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateFoo(foo: Foo): Mono<Void> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Imperative and reactive transaction management share the same semantics for transaction
|
||||||
|
boundary and transaction attribute definitions. The main difference between imperative
|
||||||
|
and reactive transactions is the deferred nature of the latter. `TransactionInterceptor`
|
||||||
|
decorates the returned reactive type with a transactional operator to begin and clean up
|
||||||
|
the transaction. Therefore, calling a transactional reactive method defers the actual
|
||||||
|
transaction management to a subscription type that activates processing of the reactive
|
||||||
|
type.
|
||||||
|
|
||||||
|
Another aspect of reactive transaction management relates to data escaping which is a
|
||||||
|
natural consequence of the programming model.
|
||||||
|
|
||||||
|
Method return values of imperative transactions are returned from transactional methods
|
||||||
|
upon successful termination of a method so that partially computed results do not escape
|
||||||
|
the method closure.
|
||||||
|
|
||||||
|
Reactive transaction methods return a reactive wrapper type which represents a
|
||||||
|
computation sequence along with a promise to begin and complete the computation.
|
||||||
|
|
||||||
|
A `Publisher` can emit data while a transaction is ongoing but not necessarily completed.
|
||||||
|
Therefore, methods that depend upon successful completion of an entire transaction need
|
||||||
|
to ensure completion and buffer results in the calling code.
|
||||||
|
|
||||||
|
|
||||||
[[transaction-declarative-rolling-back]]
|
[[transaction-declarative-rolling-back]]
|
||||||
==== Rolling Back a Declarative Transaction
|
==== Rolling Back a Declarative Transaction
|
||||||
|
@ -1218,11 +1399,63 @@ 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/>` tag
|
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
|
||||||
if the bean name of the `PlatformTransactionManager` 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 `PlatformTransactionManager` bean that you want to
|
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
|
||||||
dependency-inject has any other name, you have to use the `transaction-manager` attribute,
|
has any other name, you have to use the `transaction-manager` attribute, as in the
|
||||||
as in the preceding example.
|
preceding example.
|
||||||
|
|
||||||
|
Reactive transactional methods use reactive return types in contrast to imperative
|
||||||
|
programming arrangements as the following listing shows:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
// the reactive service class that we want to make transactional
|
||||||
|
@Transactional
|
||||||
|
public class DefaultFooService implements FooService {
|
||||||
|
|
||||||
|
Publisher<Foo> getFoo(String fooName) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
Mono<Foo> getFoo(String fooName, String barName) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
Mono<Void> insertFoo(Foo foo) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
Mono<Void> updateFoo(Foo foo) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
// the reactive service class that we want to make transactional
|
||||||
|
@Transactional
|
||||||
|
class DefaultFooService : FooService {
|
||||||
|
|
||||||
|
override fun getFoo(fooName: String): Flow<Foo> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFoo(fooName: String, barName: String): Mono<Foo> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun insertFoo(foo: Foo): Mono<Void> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateFoo(foo: Foo): Mono<Void> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
.Method visibility and `@Transactional`
|
.Method visibility and `@Transactional`
|
||||||
****
|
****
|
||||||
|
@ -1445,10 +1678,10 @@ Most Spring applications need only a single transaction manager, but there may b
|
||||||
situations where you want multiple independent transaction managers in a single
|
situations where you want multiple independent transaction managers in a single
|
||||||
application. You can use the `value` or `transactionManager` attribute of the
|
application. You can use the `value` or `transactionManager` attribute of the
|
||||||
`@Transactional` annotation to optionally specify the identity of the
|
`@Transactional` annotation to optionally specify the identity of the
|
||||||
`PlatformTransactionManager` to be used. This can either be the bean name or the
|
`TransactionManager` to be used. This can either be the bean name or the qualifier value
|
||||||
qualifier value of the transaction manager bean. For example, using the qualifier
|
of the transaction manager bean. For example, using the qualifier notation, you can
|
||||||
notation, you can combine the following Java code with the following transaction manager
|
combine the following Java code with the following transaction manager bean declarations
|
||||||
bean declarations in the application context:
|
in the application context:
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
.Java
|
.Java
|
||||||
|
@ -1460,6 +1693,9 @@ bean declarations in the application context:
|
||||||
|
|
||||||
@Transactional("account")
|
@Transactional("account")
|
||||||
public void doSomething() { ... }
|
public void doSomething() { ... }
|
||||||
|
|
||||||
|
@Transactional("reactive-account")
|
||||||
|
public Mono<Void> doSomethingReactive() { ... }
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||||
|
@ -1476,6 +1712,11 @@ bean declarations in the application context:
|
||||||
fun doSomething() {
|
fun doSomething() {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional("reactive-account")
|
||||||
|
fun doSomethingReactive(): Mono<Void> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -1494,12 +1735,17 @@ The following listing shows the bean declarations:
|
||||||
...
|
...
|
||||||
<qualifier value="account"/>
|
<qualifier value="account"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
|
||||||
|
...
|
||||||
|
<qualifier value="reactive-account"/>
|
||||||
|
</bean>
|
||||||
----
|
----
|
||||||
|
|
||||||
In this case, the two methods on `TransactionalService` run under separate transaction
|
In this case, the individual methods on `TransactionalService` run under separate
|
||||||
managers, differentiated by the `order` and `account` qualifiers. The default
|
transaction managers, differentiated by the `order`, `account`, and `reactive-account`
|
||||||
`<tx:annotation-driven>` target bean name, `transactionManager`, is still used if no
|
qualifiers. The default `<tx:annotation-driven>` target bean name, `transactionManager`,
|
||||||
specifically qualified `PlatformTransactionManager` bean is found.
|
is still used if no specifically qualified `TransactionManager` bean is found.
|
||||||
|
|
||||||
[[tx-custom-attributes]]
|
[[tx-custom-attributes]]
|
||||||
===== Custom Composed Annotations
|
===== Custom Composed Annotations
|
||||||
|
@ -1845,7 +2091,7 @@ declarative approach:
|
||||||
</tx:attributes>
|
</tx:attributes>
|
||||||
</tx:advice>
|
</tx:advice>
|
||||||
|
|
||||||
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
|
<!-- other <bean/> definitions such as a DataSource and a TransactionManager here -->
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
----
|
----
|
||||||
|
@ -1925,12 +2171,13 @@ AspectJ in the Spring Framework>> for a discussion of load-time weaving with Asp
|
||||||
|
|
||||||
The Spring Framework provides two means of programmatic transaction management, by using:
|
The Spring Framework provides two means of programmatic transaction management, by using:
|
||||||
|
|
||||||
* The `TransactionTemplate`.
|
* The `TransactionTemplate` or `TransactionalOperator`.
|
||||||
* A `PlatformTransactionManager` implementation directly.
|
* A `TransactionManager` implementation directly.
|
||||||
|
|
||||||
The Spring team generally recommends the `TransactionTemplate` for programmatic
|
The Spring team generally recommends the `TransactionTemplate` for programmatic
|
||||||
transaction management. The second approach is similar to using the JTA
|
transaction management in imperative flows and `TransactionalOperator` for reactive code.
|
||||||
`UserTransaction` API, although exception handling is less cumbersome.
|
The second approach is similar to using the JTA `UserTransaction` API, although exception
|
||||||
|
handling is less cumbersome.
|
||||||
|
|
||||||
|
|
||||||
[[tx-prog-template]]
|
[[tx-prog-template]]
|
||||||
|
@ -2117,15 +2364,152 @@ of a `TransactionTemplate`, if a class needs to use a `TransactionTemplate` with
|
||||||
different settings (for example, a different isolation level), you need to create
|
different settings (for example, a different isolation level), you need to create
|
||||||
two distinct `TransactionTemplate` instances.
|
two distinct `TransactionTemplate` instances.
|
||||||
|
|
||||||
|
[[tx-prog-operator]]
|
||||||
|
==== Using the `TransactionOperator`
|
||||||
|
|
||||||
|
The `TransactionOperator` follows an operator design that is similar to other reactive
|
||||||
|
operators. It uses a callback approach (to free application code from having to do the
|
||||||
|
boilerplate acquisition and release transactional resources) and results in code that is
|
||||||
|
intention driven, in that your code focuses solely on what you want to do.
|
||||||
|
|
||||||
|
NOTE: As the examples that follow show, using the `TransactionOperator` absolutely
|
||||||
|
couples you to Spring's transaction infrastructure and APIs. Whether or not programmatic
|
||||||
|
transaction management is suitable for your development needs is a decision that you have
|
||||||
|
to make yourself.
|
||||||
|
|
||||||
|
Application code that must execute in a transactional context and that explicitly uses
|
||||||
|
the `TransactionOperator` resembles the next example:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
public class SimpleService implements Service {
|
||||||
|
|
||||||
|
// single TransactionOperator shared amongst all methods in this instance
|
||||||
|
private final TransactionalOperator transactionalOperator;
|
||||||
|
|
||||||
|
// use constructor-injection to supply the ReactiveTransactionManager
|
||||||
|
public SimpleService(ReactiveTransactionManager transactionManager) {
|
||||||
|
this.transactionOperator = TransactionalOperator.create(transactionManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Object> someServiceMethod() {
|
||||||
|
|
||||||
|
// the code in this method executes in a transactional context
|
||||||
|
|
||||||
|
Mono<Object> update = updateOperation1();
|
||||||
|
|
||||||
|
return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
// use constructor-injection to supply the ReactiveTransactionManager
|
||||||
|
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
|
||||||
|
|
||||||
|
// single TransactionalOperator shared amongst all methods in this instance
|
||||||
|
private val transactionalOperator = TransactionalOperator.create(transactionManager)
|
||||||
|
|
||||||
|
suspend fun someServiceMethod() = transactionalOperator.executeAndAwait<Any?> {
|
||||||
|
updateOperation1()
|
||||||
|
resultOfUpdateOperation2()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
`TransactionalOperator` can be used in two ways:
|
||||||
|
|
||||||
|
* Operator-style using Project Reactor types (`mono.as(transactionalOperator::transactional)`)
|
||||||
|
* Callback-style for every other case (`transactionalOperator.execute(TransactionCallback<T>)`)
|
||||||
|
|
||||||
|
Code within the callback can roll the transaction back by calling the `setRollbackOnly()`
|
||||||
|
method on the supplied `ReactiveTransaction` object, as follows:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
transactionalOperator.execute(new TransactionCallback<>() {
|
||||||
|
|
||||||
|
public Mono<Object> doInTransaction(ReactiveTransaction status) {
|
||||||
|
return updateOperation1().then(updateOperation2)
|
||||||
|
.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
transactionalOperator.execute(object : TransactionCallback() {
|
||||||
|
|
||||||
|
override fun doInTransactionWithoutResult(status: ReactiveTransaction) {
|
||||||
|
updateOperation1().then(updateOperation2)
|
||||||
|
.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
----
|
||||||
|
|
||||||
|
[[tx-prog-operator-settings]]
|
||||||
|
===== Specifying Transaction Settings
|
||||||
|
|
||||||
|
You can specify transaction settings (such as the propagation mode, the isolation level,
|
||||||
|
the timeout, and so forth) for the `TransactionalOperator`. By default,
|
||||||
|
`TransactionalOperator` instances have
|
||||||
|
<<transaction-declarative-txadvice-settings,default transactional settings>>. The
|
||||||
|
following example shows customization of the transactional settings for a specific
|
||||||
|
`TransactionalOperator:`
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
public class SimpleService implements Service {
|
||||||
|
|
||||||
|
private final TransactionalOperator transactionalOperator;
|
||||||
|
|
||||||
|
public SimpleService(ReactiveTransactionManager transactionManager) {
|
||||||
|
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
|
||||||
|
|
||||||
|
// the transaction settings can be set here explicitly if so desired
|
||||||
|
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
|
||||||
|
definition.setTimeout(30); // 30 seconds
|
||||||
|
// and so forth...
|
||||||
|
|
||||||
|
this.transactionalOperator = TransactionalOperator.create(transactionManager, definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
|
||||||
|
|
||||||
|
private val definition = DefaultTransactionDefinition().apply {
|
||||||
|
// the transaction settings can be set here explicitly if so desired
|
||||||
|
isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED
|
||||||
|
timeout = 30 // 30 seconds
|
||||||
|
// and so forth...
|
||||||
|
}
|
||||||
|
private val transactionalOperator = TransactionalOperator(transactionManager, definition)
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[[transaction-programmatic-tm]]
|
||||||
|
==== Using the `TransactionManager`
|
||||||
|
|
||||||
|
The following sections explain programmatic usage of imperative and reactive transaction
|
||||||
|
managers.
|
||||||
|
|
||||||
[[transaction-programmatic-ptm]]
|
[[transaction-programmatic-ptm]]
|
||||||
==== Using the `PlatformTransactionManager`
|
===== Using the `PlatformTransactionManager`
|
||||||
|
|
||||||
You can also use the `org.springframework.transaction.PlatformTransactionManager`
|
For imperative transactions, you can use a
|
||||||
directly to manage your transaction. To do so, pass the implementation of the
|
`org.springframework.transaction.PlatformTransactionManager` directly to manage your
|
||||||
`PlatformTransactionManager` you use to your bean through a bean reference. Then,
|
transaction. To do so, pass the implementation of the `PlatformTransactionManager` you
|
||||||
by using the `TransactionDefinition` and `TransactionStatus` objects, you can initiate
|
use to your bean through a bean reference. Then, by using the `TransactionDefinition` and
|
||||||
transactions, roll back, and commit. The following example shows how to do so:
|
`TransactionStatus` objects, you can initiate transactions, roll back, and commit. The
|
||||||
|
following example shows how to do so:
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
.Java
|
.Java
|
||||||
|
@ -2165,6 +2549,52 @@ transactions, roll back, and commit. The following example shows how to do so:
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
[[transaction-programmatic-rtm]]
|
||||||
|
===== Using the `ReactiveTransactionManager`
|
||||||
|
|
||||||
|
When working with reactive transactions, you can use a
|
||||||
|
`org.springframework.transaction.ReactiveTransactionManager` directly to manage your
|
||||||
|
transaction. To do so, pass the implementation of the `ReactiveTransactionManager` you
|
||||||
|
use to your bean through a bean reference. Then, by using the `TransactionDefinition` and
|
||||||
|
`ReactiveTransaction` objects, you can initiate transactions, roll back, and commit. The
|
||||||
|
following example shows how to do so:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
|
||||||
|
// explicitly setting the transaction name is something that can be done only programmatically
|
||||||
|
def.setName("SomeTxName");
|
||||||
|
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||||
|
|
||||||
|
Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def);
|
||||||
|
|
||||||
|
reactiveTx.flatMap(status -> {
|
||||||
|
|
||||||
|
Mono<Object> tx = ...; // execute your business logic here
|
||||||
|
|
||||||
|
return tx.then(txManager.commit(status))
|
||||||
|
.onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex)));
|
||||||
|
});
|
||||||
|
----
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
val def = DefaultTransactionDefinition()
|
||||||
|
// explicitly setting the transaction name is something that can be done only programmatically
|
||||||
|
def.setName("SomeTxName")
|
||||||
|
def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED
|
||||||
|
|
||||||
|
val reactiveTx = txManager.getReactiveTransaction(def)
|
||||||
|
reactiveTx.flatMap { status ->
|
||||||
|
|
||||||
|
val tx = ... // execute your business logic here
|
||||||
|
|
||||||
|
tx.then(txManager.commit(status))
|
||||||
|
.onErrorResume { ex -> txManager.rollback(status).then(Mono.error(ex)) }
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
[[tx-decl-vs-prog]]
|
[[tx-decl-vs-prog]]
|
||||||
=== Choosing Between Programmatic and Declarative Transaction Management
|
=== Choosing Between Programmatic and Declarative Transaction Management
|
||||||
|
|
|
@ -30,9 +30,9 @@ Alef Arendsen, Darren Davison, Dmitriy Kopylenko, Mark Pollack, Thierry Templier
|
||||||
Vervaet, Portia Tung, Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
|
Vervaet, Portia Tung, Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
|
||||||
Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clement, Dave
|
Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clement, Dave
|
||||||
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
|
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
|
||||||
Nicoll, Sebastien Deleuze, Jay Bryant
|
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
|
||||||
|
|
||||||
Copyright © 2002 - 2019 Pivotal, Inc. All Rights Reserved.
|
Copyright © 2002 - 2020 Pivotal, Inc. All Rights Reserved.
|
||||||
|
|
||||||
Copies of this document may be made for your own use and for distribution to others,
|
Copies of this document may be made for your own use and for distribution to others,
|
||||||
provided that you do not charge any fee for such copies and further provided that each
|
provided that you do not charge any fee for such copies and further provided that each
|
||||||
|
|
Loading…
Reference in New Issue