From 7fbdc3ad11b7cada0234d837b9a2acc81e12edd7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 8 May 2020 12:19:49 +0200 Subject: [PATCH 1/2] Document ReactiveTransactionManager support in reference manual See gh-25030. --- src/docs/asciidoc/data-access.adoc | 491 +++++++++++++++++++++++++++-- src/docs/asciidoc/index.adoc | 4 +- 2 files changed, 460 insertions(+), 35 deletions(-) diff --git a/src/docs/asciidoc/data-access.adoc b/src/docs/asciidoc/data-access.adoc index beef7b7235..fbd4134548 100644 --- a/src/docs/asciidoc/data-access.adoc +++ b/src/docs/asciidoc/data-access.adoc @@ -151,13 +151,14 @@ configuration file need to change (rather than your code). === Understanding the Spring Framework Transaction Abstraction The key to the Spring transaction abstraction is the notion of a transaction -strategy. A transaction strategy is defined by the -`org.springframework.transaction.PlatformTransactionManager` interface, which the following listing shows: +strategy. A transaction strategy is defined by a `TransactionManager`, specifically +the `org.springframework.transaction.PlatformTransactionManager` interface for +imperative transaction management which the following listing shows: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java ---- - public interface PlatformTransactionManager { + public interface PlatformTransactionManager extends TransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; @@ -169,7 +170,7 @@ strategy. A transaction strategy is defined by the [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] .Kotlin ---- - interface PlatformTransactionManager { + interface PlatformTransactionManager : TransactionManager { @Throws(TransactionException::class) fun getTransaction(definition: TransactionDefinition): TransactionStatus @@ -206,6 +207,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 execution. +Spring Framework 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 getReactiveTransaction(TransactionDefinition definition) throws TransactionException; + + Mono commit(ReactiveTransaction status) throws TransactionException; + + Mono 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 + + @Throws(TransactionException::class) + fun commit(status: ReactiveTransaction): Mono + + @Throws(TransactionException::class) + fun rollback(status: ReactiveTransaction): Mono + } +---- + +The reactive transaction manager is primarily a service provider interface (SPI), +although you can use it <> from your +application code. Because `ReactiveTransactionManager` is an interface, it can +be easily mocked or stubbed as necessary. + The `TransactionDefinition` interface specifies: * Propagation: Typically, all code executed within a transaction scope runs in @@ -237,45 +276,49 @@ familiar, as they are common to all transaction APIs. The following listing show [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java ---- - public interface TransactionStatus extends SavepointManager { + public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable { + @Override boolean isNewTransaction(); boolean hasSavepoint(); + @Override void setRollbackOnly(); + @Override boolean isRollbackOnly(); void flush(); + @Override boolean isCompleted(); } ---- [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] .Kotlin ---- - interface TransactionStatus : SavepointManager { + interface TransactionStatus : TransactionExecution, SavepointManager, Flushable { - fun isNewTransaction(): Boolean + override fun isNewTransaction(): Boolean fun hasSavepoint(): Boolean - fun setRollbackOnly() + override fun setRollbackOnly() - fun isRollbackOnly(): Boolean + override fun isRollbackOnly(): Boolean fun flush() - fun isCompleted(): Boolean + override fun isCompleted(): Boolean } ---- 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. You typically define this implementation through dependency injection. -`PlatformTransactionManager` implementations normally require knowledge of the +`TransactionManager` implementations normally require knowledge of the environment in which they work: JDBC, JTA, Hibernate, and so on. The following examples show how you can define a local `PlatformTransactionManager` implementation (in this case, with plain JDBC.) @@ -402,7 +445,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) ensures that these resources are created, reused, and cleaned up properly. The section also discusses how transaction synchronization is (optionally) triggered through the -relevant `PlatformTransactionManager`. +relevant `TransactionManager`. [[tx-resource-synchronization-high]] @@ -542,11 +585,22 @@ transaction support are that this support is enabled <> and that the transactional advice is driven by metadata (currently XML- or annotation-based). The combination of AOP with transactional metadata yields an AOP proxy that uses a `TransactionInterceptor` -in conjunction with an appropriate `PlatformTransactionManager` implementation to drive +in conjunction with an appropriate `TransactionManager` implementation to drive transactions around method invocations. NOTE: Spring AOP is covered in <>. +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 impacts which transaction manager required. +Imperative transactions require a `PlatformTransactionManager` while reactive transactions +use `ReactiveTransactionManager` implementations. + The following images shows a Conceptual view of calling a method on a transactional proxy: image::images/tx.png[] @@ -707,7 +761,7 @@ configuration is explained in detail in the next few paragraphs: - + @@ -723,12 +777,12 @@ bean, transactional. The transaction semantics to apply are encapsulated in the 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 `transaction-manager` attribute of the `` 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 `txManager` bean). TIP: You can omit the `transaction-manager` attribute in the transactional advice -(``) if the bean name of the `PlatformTransactionManager` that you want to -wire in has the name `transactionManager`. If the `PlatformTransactionManager` bean that +(``) 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 wire in has any other name, you must use the `transaction-manager` attribute explicitly, as in the preceding example. @@ -826,6 +880,128 @@ insertFoo(..) method of the DefaultFooService class have been truncated for clar at Boot.main(Boot.java:11) ---- +To use reactive transaction management the code has to use reactive types. + +NOTES: Spring Framework uses `ReactiveAdapterRegistry` to determine whether a +method return type is reactive. + +The following listing shows the previously used `FooService` but this time the code is using 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 getFoo(String fooName); + + Publisher getFoo(String fooName, String barName); + + Mono insertFoo(Foo foo); + + Mono 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 + + fun getFoo(fooName: String, barName: String): Publisher + + fun insertFoo(foo: Foo) : Mono + + fun updateFoo(foo: Foo) + } +---- + +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 getFoo(String fooName) { + // ... + } + + @Override + public Publisher getFoo(String fooName, String barName) { + // ... + } + + @Override + public Mono insertFoo(Foo foo) { + // ... + } + + @Override + public Mono 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 { + // ... + } + + override fun getFoo(fooName: String, barName: String): Publisher { + // ... + } + + override fun insertFoo(foo: Foo): Mono { + // ... + } + + override fun updateFoo(foo: Foo): Mono { + // ... + } + } +---- + +Imperative and reactive transaction management shares the same semantics for +transaction boundary and transaction attribute definitions. The main difference +to imperative transactions is the deferred nature. `TransactionInterceptor` +decorates the returned reactive type with a transactional operator +to begin and cleanup the transaction. Therefore, calling a transactional +reactive method defers the actual transaction management to 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 represent a +computation sequence along with a promise to begin and complete computation. + +A `Publisher` can emit data while an 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]] ==== Rolling Back a Declarative Transaction @@ -1220,10 +1396,62 @@ In XML configuration, the `` tag provides similar conveni TIP: You can omit the `transaction-manager` attribute in the `` tag if the bean name of the `PlatformTransactionManager` 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 has any other name, you have to use the `transaction-manager` attribute, as in the 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 getFoo(String fooName) { + // ... + } + + Mono getFoo(String fooName, String barName) { + // ... + } + + Mono insertFoo(Foo foo) { + // ... + } + + Mono 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 { + // ... + } + + override fun getFoo(fooName: String, barName: String): Mono { + // ... + } + + override fun insertFoo(foo: Foo): Mono { + // ... + } + + override fun updateFoo(foo: Foo): Mono { + // ... + } + } +---- + .Method visibility and `@Transactional` **** When you use proxies, you should apply the `@Transactional` annotation only to methods @@ -1438,11 +1666,12 @@ name of the transaction would be: `com.example.BusinessService.handlePayment`. Most Spring applications need only a single transaction manager, but there may be situations where you want multiple independent transaction managers in a single -application. You can use the `value` attribute of the `@Transactional` annotation to -optionally specify the identity of the `PlatformTransactionManager` to be used. -This can either be the bean name or the qualifier value of the transaction manager bean. -For example, using the qualifier notation, you can combine the following Java code with -the following transaction manager bean declarations in the application context: +application. You can use the `value` or `transactionManager` attribute of the +`@Transactional` annotation to optionally specify the identity of the +`TransactionManager` to be used. This can either be the bean name or the +qualifier value of the transaction manager bean. For example, using the qualifier +notation, you can combine the following Java code with the following transaction manager +bean declarations in the application context: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java @@ -1454,6 +1683,9 @@ the following transaction manager bean declarations in the application context: @Transactional("account") public void doSomething() { ... } + + @Transactional("reactive-account") + public Mono doSomethingReactive() { ... } } ---- [source,kotlin,indent=0,subs="verbatim",role="secondary"] @@ -1470,6 +1702,11 @@ the following transaction manager bean declarations in the application context: fun doSomething() { // ... } + + @Transactional("reactive-account") + fun doSomethingReactive(): Mono { + // ... + } } ---- @@ -1488,12 +1725,17 @@ The following listing shows the bean declarations: ... + + + ... + + ---- -In this case, the two methods on `TransactionalService` run under separate transaction -managers, differentiated by the `order` and `account` qualifiers. The default +In this case, the individual methods on `TransactionalService` run under separate transaction +managers, differentiated by the `order`, `account`, and `reactive-account` qualifiers. The default `` target bean name, `transactionManager`, is still used if no -specifically qualified `PlatformTransactionManager` bean is found. +specifically qualified `TransactionManager` bean is found. [[tx-custom-attributes]] ===== Custom Shortcut Annotations @@ -1838,7 +2080,7 @@ declarative approach: - + ---- @@ -1918,11 +2160,12 @@ 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 `TransactionTemplate`. -* A `PlatformTransactionManager` implementation directly. +* The `TransactionTemplate` or `TransactionalOperator`. +* A `TransactionManager` implementation directly. 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. +The second approach is similar to using the JTA `UserTransaction` API, although exception handling is less cumbersome. @@ -2110,11 +2353,147 @@ of a `TransactionTemplate`, if a class needs to use a `TransactionTemplate` with different settings (for example, a different isolation level), you need to create 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 someServiceMethod() { + + // the code in this method executes in a transactional context + + Mono 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 { + 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)`) + +Code within the callback can roll the transaction back by calling the +`setRollbackOnly()` method on the supplied `TransactionStatus` object, as follows: + +[source,java,indent=0,subs="verbatim,quotes",role="primary"] +.Java +---- + transactionalOperator.execute(new TransactionCallback<>() { + + public Mono 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 the +<>. 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 section explains programmatic usage of imperative and reactive transaction managers. [[transaction-programmatic-ptm]] -==== Using the `PlatformTransactionManager` +===== Using the `PlatformTransactionManager` -You can also use the `org.springframework.transaction.PlatformTransactionManager` +For imperative transactions, you can use the `org.springframework.transaction.PlatformTransactionManager` directly to manage your transaction. To do so, pass the implementation of the `PlatformTransactionManager` you use to your bean through a bean reference. Then, by using the `TransactionDefinition` and `TransactionStatus` objects, you can initiate @@ -2158,6 +2537,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 +`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 reactiveTx = txManager.getReactiveTransaction(def); + + reactiveTx.flatMap(status -> { + + Mono 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]] === Choosing Between Programmatic and Declarative Transaction Management diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 7d6ec19181..800c813824 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -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 Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clement, Dave 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, provided that you do not charge any fee for such copies and further provided that each From e1b2cafb335e3a98ce807cd8fdc2cd28c7f1ee17 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 8 May 2020 18:13:31 +0200 Subject: [PATCH 2/2] Polish reactive transaction mgmt documentation in reference manual See gh-25030 --- src/docs/asciidoc/data-access.adoc | 271 +++++++++++++++-------------- 1 file changed, 139 insertions(+), 132 deletions(-) diff --git a/src/docs/asciidoc/data-access.adoc b/src/docs/asciidoc/data-access.adoc index fbd4134548..e886e7bc95 100644 --- a/src/docs/asciidoc/data-access.adoc +++ b/src/docs/asciidoc/data-access.adoc @@ -150,10 +150,13 @@ configuration file need to change (rather than your code). [[transaction-strategies]] === Understanding the Spring Framework Transaction Abstraction -The key to the Spring transaction abstraction is the notion of a transaction -strategy. A transaction strategy is defined by a `TransactionManager`, specifically -the `org.springframework.transaction.PlatformTransactionManager` interface for -imperative transaction management which the following listing shows: +The key to the Spring transaction abstraction is the notion of a transaction strategy. A +transaction strategy is defined by a `TransactionManager`, specifically the +`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"] .Java @@ -189,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. `PlatformTransactionManager` implementations are defined like any other object (or bean) 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 -much more easily than if it used JTA directly. +transactions a worthwhile abstraction, even when you work with JTA. You can test +transactional code much more easily than if it used JTA directly. Again, in keeping with Spring's philosophy, the `TransactionException` that can be thrown by any of the `PlatformTransactionManager` interface's methods is unchecked (that @@ -207,9 +210,9 @@ 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 execution. -Spring Framework 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 +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"] @@ -240,10 +243,10 @@ shows the transaction strategy defined by } ---- -The reactive transaction manager is primarily a service provider interface (SPI), -although you can use it <> from your -application code. Because `ReactiveTransactionManager` is an interface, it can -be easily mocked or stubbed as necessary. +The reactive transaction manager is primarily a service provider interface (SPI), +although you can use it <> from your +application code. Because `ReactiveTransactionManager` is an interface, it can be easily +mocked or stubbed as necessary. The `TransactionDefinition` interface specifies: @@ -315,13 +318,13 @@ familiar, as they are common to all transaction APIs. The following listing show ---- Regardless of whether you opt for declarative or programmatic transaction management in -Spring, defining the correct `TransactionManager` implementation is absolutely -essential. You typically define this implementation through dependency injection. +Spring, defining the correct `TransactionManager` implementation is absolutely essential. +You typically define this implementation through dependency injection. -`TransactionManager` implementations normally require knowledge of the -environment in which they work: JDBC, JTA, Hibernate, and so on. The following examples -show how you can define a local `PlatformTransactionManager` implementation (in this case, -with plain JDBC.) +`TransactionManager` implementations normally require knowledge of the environment in +which they work: JDBC, JTA, Hibernate, and so on. The following examples show how you can +define a local `PlatformTransactionManager` implementation (in this case, with plain +JDBC.) You can define a JDBC `DataSource` by creating a bean similar to the following: @@ -335,8 +338,8 @@ You can define a JDBC `DataSource` by creating a bean similar to the following: ---- -The related `PlatformTransactionManager` bean definition then has a reference to -the `DataSource` definition. It should resemble the following example: +The related `PlatformTransactionManager` bean definition then has a reference to the +`DataSource` definition. It should resemble the following example: [source,xml,indent=0,subs="verbatim,quotes"] ---- @@ -346,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 -through JNDI, in conjunction with Spring's `JtaTransactionManager`. The following example shows what the JTA -and JNDI lookup version would look like: +through JNDI, in conjunction with Spring's `JtaTransactionManager`. The following example +shows what the JTA and JNDI lookup version would look like: [source,xml,indent=0,subs="verbatim,quotes"] ---- @@ -378,21 +381,21 @@ NOTE: The preceding definition of the `dataSource` bean uses the ` from the `jee` namespace. For more information see <>. -You can also use easily Hibernate local transactions, as shown in the following -examples. In this case, you need to define a Hibernate `LocalSessionFactoryBean`, -which your application code can use to obtain Hibernate `Session` instances. +You can also easily use Hibernate local transactions, as shown in the following examples. +In this case, you need to define a Hibernate `LocalSessionFactoryBean`, which your +application code can use to obtain Hibernate `Session` instances. -The `DataSource` bean definition is similar to the local JDBC example shown -previously and, thus, is not shown in the following example. +The `DataSource` bean definition is similar to the local JDBC example shown previously +and, thus, is not shown in the following example. 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 Spring Framework (rather than the Java EE container) manages the transactions. The `txManager` bean in this case is of the `HibernateTransactionManager` type. In the -same way as the `DataSourceTransactionManager` needs a reference to the `DataSource`, -the `HibernateTransactionManager` needs a reference to the `SessionFactory`. -The following example declares `sessionFactory` and `txManager` beans: +same way as the `DataSourceTransactionManager` needs a reference to the `DataSource`, the +`HibernateTransactionManager` needs a reference to the `SessionFactory`. The following +example declares `sessionFactory` and `txManager` beans: [source,xml,indent=0,subs="verbatim,quotes"] ---- @@ -415,9 +418,9 @@ The following example declares `sessionFactory` and `txManager` beans: ---- -If you use Hibernate and Java EE container-managed JTA transactions, you -should use the same `JtaTransactionManager` as in the previous JTA example for -JDBC, as the following example shows: +If you use Hibernate and Java EE container-managed JTA transactions, you should use the +same `JtaTransactionManager` as in the previous JTA example for JDBC, as the following +example shows: [source,xml,indent=0,subs="verbatim,quotes"] ---- @@ -459,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 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 -chapters of this reference documentation. +sections of this reference documentation. [[tx-resource-synchronization-low]] @@ -576,32 +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 `@Transactional` annotation, add `@EnableTransactionManagement` to your configuration, -and expect you to understand how it all works. To provide a deeper understanding, -this section explains the inner workings of the Spring Framework's declarative -transaction infrastructure in the event of transaction-related issues. +and expect you to understand how it all works. To provide a deeper understanding, this +section explains the inner workings of the Spring Framework's declarative transaction +infrastructure in the context of transaction-related issues. The most important concepts to grasp with regard to the Spring Framework's declarative transaction support are that this support is enabled <> and that the transactional -advice is driven by metadata (currently XML- or annotation-based). The combination of -AOP with transactional metadata yields an AOP proxy that uses a `TransactionInterceptor` -in conjunction with an appropriate `TransactionManager` implementation to drive -transactions around method invocations. +advice is driven by metadata (currently XML- or annotation-based). The combination of AOP +with transactional metadata yields an AOP proxy that uses a `TransactionInterceptor` in +conjunction with an appropriate `TransactionManager` implementation to drive transactions +around method invocations. NOTE: Spring AOP is covered in <>. -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. +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 impacts which transaction manager required. -Imperative transactions require a `PlatformTransactionManager` while reactive transactions -use `ReactiveTransactionManager` implementations. +Transaction management flavors impact which transaction manager is required. Imperative +transactions require a `PlatformTransactionManager`, while reactive transactions use +`ReactiveTransactionManager` implementations. -The following images shows a Conceptual view of calling a method on a transactional proxy: +The following image shows a conceptual view of calling a method on a transactional proxy: image::images/tx.png[] @@ -614,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, the fact that the `DefaultFooService` class throws `UnsupportedOperationException` 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 -`UnsupportedOperationException` instance. The following listing shows the `FooService` interface: +transactions being created and then rolled back in response to the +`UnsupportedOperationException` instance. The following listing shows the `FooService` +interface: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java @@ -771,14 +775,14 @@ configuration is explained in detail in the next few paragraphs: ---- -Examine the preceding configuration. It assumes that you want to make a service object, the `fooService` -bean, transactional. The transaction semantics to apply are encapsulated in the -`` definition. The `` definition reads as "`all methods, on -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 +Examine the preceding configuration. It assumes that you want to make a service object, +the `fooService` bean, transactional. The transaction semantics to apply are encapsulated +in the `` definition. The `` definition reads as "all methods +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 `transaction-manager` attribute of the `` tag is set to the name of the -`TransactionManager` bean that is going to drive the transactions (in this -case, the `txManager` bean). +`TransactionManager` bean that is going to drive the transactions (in this case, the +`txManager` bean). TIP: You can omit the `transaction-manager` attribute in the transactional advice (``) if the bean name of the `TransactionManager` that you want to @@ -813,7 +817,7 @@ NOTE: In the preceding example, it is assumed that all your service interfaces a in the `x.y.service` package. See <> for more details. 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 that is created from the `fooService` bean definition. The proxy is configured with @@ -847,8 +851,8 @@ that test drives the configuration shown earlier: ---- The output from running the preceding program should resemble the following (the Log4J -output and the stack trace from the UnsupportedOperationException thrown by the -insertFoo(..) method of the DefaultFooService class have been truncated for clarity): +output and the stack trace from the `UnsupportedOperationException` thrown by the +`insertFoo(..)` method of the `DefaultFooService` class have been truncated for clarity): [source,xml,indent=0,subs="verbatim,quotes"] ---- @@ -882,10 +886,11 @@ insertFoo(..) method of the DefaultFooService class have been truncated for clar To use reactive transaction management the code has to use reactive types. -NOTES: Spring Framework uses `ReactiveAdapterRegistry` to determine whether a -method return type is reactive. +NOTE: Spring Framework uses the `ReactiveAdapterRegistry` to determine whether a method +return type is reactive. -The following listing shows the previously used `FooService` but this time the code is using reactive types: +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 @@ -921,7 +926,7 @@ The following listing shows the previously used `FooService` but this time the c fun insertFoo(foo: Foo) : Mono - fun updateFoo(foo: Foo) + fun updateFoo(foo: Foo) : Mono } ---- @@ -980,27 +985,27 @@ The following example shows an implementation of the preceding interface: } ---- -Imperative and reactive transaction management shares the same semantics for -transaction boundary and transaction attribute definitions. The main difference -to imperative transactions is the deferred nature. `TransactionInterceptor` -decorates the returned reactive type with a transactional operator -to begin and cleanup the transaction. Therefore, calling a transactional -reactive method defers the actual transaction management to subscription -type that activates processing of the reactive type. +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. +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. +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 represent a -computation sequence along with a promise to begin and complete computation. +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 an 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. +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]] @@ -1394,14 +1399,14 @@ In XML configuration, the `` tag provides similar conveni <1> The line that makes the bean instance transactional. -TIP: You can omit the `transaction-manager` attribute in the `` tag -if the bean name of the `PlatformTransactionManager` that you want to wire in has the name, -`transactionManager`. If the `TransactionManager` bean that you want to -dependency-inject has any other name, you have to use the `transaction-manager` attribute, -as in the preceding example. +TIP: You can omit the `transaction-manager` attribute in the `` +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 +has any other name, you have to use the `transaction-manager` attribute, as in the +preceding example. -Reactive transactional methods use reactive return types in contrast to imperative programming -arrangements as the following listing shows: +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 @@ -1668,10 +1673,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 application. You can use the `value` or `transactionManager` attribute of the `@Transactional` annotation to optionally specify the identity of the -`TransactionManager` to be used. This can either be the bean name or the -qualifier value of the transaction manager bean. For example, using the qualifier -notation, you can combine the following Java code with the following transaction manager -bean declarations in the application context: +`TransactionManager` to be used. This can either be the bean name or the qualifier value +of the transaction manager bean. For example, using the qualifier notation, you can +combine the following Java code with the following transaction manager bean declarations +in the application context: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java @@ -1732,17 +1737,17 @@ The following listing shows the bean declarations: ---- -In this case, the individual methods on `TransactionalService` run under separate transaction -managers, differentiated by the `order`, `account`, and `reactive-account` qualifiers. The default -`` target bean name, `transactionManager`, is still used if no -specifically qualified `TransactionManager` bean is found. +In this case, the individual methods on `TransactionalService` run under separate +transaction managers, differentiated by the `order`, `account`, and `reactive-account` +qualifiers. The default `` target bean name, `transactionManager`, +is still used if no specifically qualified `TransactionManager` bean is found. [[tx-custom-attributes]] -===== Custom Shortcut Annotations +===== Custom Composed Annotations If you find you repeatedly use the same attributes with `@Transactional` on many different methods, <> lets you -define custom shortcut annotations for your specific use cases. For example, consider the +define custom composed annotations for your specific use cases. For example, consider the following annotation definitions: [source,java,indent=0,subs="verbatim,quotes",role="primary"] @@ -1774,7 +1779,7 @@ following annotation definitions: annotation class AccountTx ---- -The preceding annotations lets us write the example from the previous section as follows: +The preceding annotations let us write the example from the previous section as follows: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java @@ -1809,8 +1814,9 @@ The preceding annotations lets us write the example from the previous section as } ---- -In the preceding example, we used the syntax to define the transaction manager qualifier, but we could also -have included propagation behavior, rollback rules, timeouts, and other features. +In the preceding example, we used the syntax to define the transaction manager qualifier, +but we could also have included propagation behavior, rollback rules, timeouts, and other +features. [[tx-propagation]] @@ -2165,8 +2171,8 @@ The Spring Framework provides two means of programmatic transaction management, The Spring team generally recommends the `TransactionTemplate` for programmatic transaction management in imperative flows and `TransactionalOperator` for reactive code. -The second approach is similar to using the JTA -`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]] @@ -2357,18 +2363,17 @@ two distinct `TransactionTemplate` instances. ==== 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. +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. +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: +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 @@ -2414,8 +2419,8 @@ Application code that must execute in a transactional context and that explicitl * Operator-style using Project Reactor types (`mono.as(transactionalOperator::transactional)`) * Callback-style for every other case (`transactionalOperator.execute(TransactionCallback)`) -Code within the callback can roll the transaction back by calling the -`setRollbackOnly()` method on the supplied `TransactionStatus` object, as follows: +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 @@ -2445,11 +2450,11 @@ Code within the callback can roll the transaction back by calling the ===== 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 the +the timeout, and so forth) for the `TransactionalOperator`. By default, +`TransactionalOperator` instances have <>. The -following example shows customization of the transactional settings for -a specific `TransactionalOperator:` +following example shows customization of the transactional settings for a specific +`TransactionalOperator:` [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java @@ -2488,16 +2493,18 @@ a specific `TransactionalOperator:` [[transaction-programmatic-tm]] ==== Using the `TransactionManager` -The following section explains programmatic usage of imperative and reactive transaction managers. +The following sections explain programmatic usage of imperative and reactive transaction +managers. [[transaction-programmatic-ptm]] ===== Using the `PlatformTransactionManager` -For imperative transactions, you can use the `org.springframework.transaction.PlatformTransactionManager` -directly to manage your transaction. To do so, pass the implementation of the -`PlatformTransactionManager` you use to your bean through a bean reference. Then, -by using the `TransactionDefinition` and `TransactionStatus` objects, you can initiate -transactions, roll back, and commit. The following example shows how to do so: +For imperative transactions, you can use a +`org.springframework.transaction.PlatformTransactionManager` directly to manage your +transaction. To do so, pass the implementation of the `PlatformTransactionManager` you +use to your bean through a bean reference. Then, by using the `TransactionDefinition` and +`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"] .Java @@ -2540,12 +2547,12 @@ 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 -`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: +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