diff --git a/src/docs/asciidoc/data-access.adoc b/src/docs/asciidoc/data-access.adoc index fbd4134548fa..e886e7bc95a4 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