Split files

This commit is contained in:
Rob Winch
2023-04-19 10:26:16 -05:00
committed by rstoyanchev
parent ac69a5dac3
commit 9f49d24833
391 changed files with 62477 additions and 62392 deletions

View File

@@ -0,0 +1,598 @@
[[transaction-declarative-annotations]]
= Using `@Transactional`
In addition to the XML-based declarative approach to transaction configuration, you can
use an annotation-based approach. Declaring transaction semantics directly in the Java
source code puts the declarations much closer to the affected code. There is not much
danger of undue coupling, because code that is meant to be used transactionally is
almost always deployed that way anyway.
NOTE: The standard `jakarta.transaction.Transactional` annotation is also supported as
a drop-in replacement to Spring's own annotation. Please refer to the JTA documentation
for more details.
The ease-of-use afforded by the use of the `@Transactional` annotation is best
illustrated with an example, which is explained in the text that follows.
Consider the following class definition:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
@Override
public Foo getFoo(String fooName) {
// ...
}
@Override
public Foo getFoo(String fooName, String barName) {
// ...
}
@Override
public void insertFoo(Foo foo) {
// ...
}
@Override
public void updateFoo(Foo foo) {
// ...
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// the service class that we want to make transactional
@Transactional
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
override fun getFoo(fooName: String, barName: String): Foo {
// ...
}
override fun insertFoo(foo: Foo) {
// ...
}
override fun updateFoo(foo: Foo) {
// ...
}
}
----
Used at the class level as above, the annotation indicates a default for all methods of
the declaring class (as well as its subclasses). Alternatively, each method can be
annotated individually. See <<transaction-declarative-annotations-method-visibility>> for
further details on which methods Spring considers transactional. Note that a class-level
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
inherited methods need to be locally redeclared in order to participate in a
subclass-level annotation.
When a POJO class such as the one above is defined as a bean in a Spring context,
you can make the bean instance transactional through an `@EnableTransactionManagement`
annotation in a `@Configuration` class. See the
{api-spring-framework}/transaction/annotation/EnableTransactionManagement.html[javadoc]
for full details.
In XML configuration, the `<tx:annotation-driven/>` tag provides similar convenience:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<!-- a TransactionManager is still required -->
<tx:annotation-driven transaction-manager="txManager"/> <1>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
----
<1> The line that makes the bean instance transactional.
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
tag if the bean name of the `TransactionManager` that you want to wire in has the name
`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 {
@Override
public Publisher<Foo> getFoo(String fooName) {
// ...
}
@Override
public Mono<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
----
// 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> {
// ...
}
}
----
Note that there are special considerations for the returned `Publisher` with regards to
Reactive Streams cancellation signals. See the <<tx-prog-operator-cancel>> section under
"Using the TransactionalOperator" for more details.
[[transaction-declarative-annotations-method-visibility]]
.Method visibility and `@Transactional`
[NOTE]
====
When you use transactional proxies with Spring's standard configuration, you should apply
the `@Transactional` annotation only to methods with `public` visibility. If you do
annotate `protected`, `private`, or package-visible methods with the `@Transactional`
annotation, no error is raised, but the annotated method does not exhibit the configured
transactional settings. If you need to annotate non-public methods, consider the tip in
the following paragraph for class-based proxies or consider using AspectJ compile-time or
load-time weaving (described later).
When using `@EnableTransactionManagement` in a `@Configuration` class, `protected` or
package-visible methods can also be made transactional for class-based proxies by
registering a custom `transactionAttributeSource` bean like in the following example.
Note, however, that transactional methods in interface-based proxies must always be
`public` and defined in the proxied interface.
[source,java,indent=0,subs="verbatim,quotes"]
----
/**
* Register a custom AnnotationTransactionAttributeSource with the
* publicMethodsOnly flag set to false to enable support for
* protected and package-private @Transactional methods in
* class-based proxies.
*
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
*/
@Bean
TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource(false);
}
----
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods by
default. See <<testing.adoc#testcontext-tx,Transaction Management>> in the testing
chapter for examples.
====
You can apply the `@Transactional` annotation to an interface definition, a method
on an interface, a class definition, or a method on a class. However, the
mere presence of the `@Transactional` annotation is not enough to activate the
transactional behavior. The `@Transactional` annotation is merely metadata that can
be consumed by some runtime infrastructure that is `@Transactional`-aware and that
can use the metadata to configure the appropriate beans with transactional behavior.
In the preceding example, the `<tx:annotation-driven/>` element switches on the
transactional behavior.
TIP: The Spring team recommends that you annotate only concrete classes (and methods of
concrete classes) with the `@Transactional` annotation, as opposed to annotating interfaces.
You certainly can place the `@Transactional` annotation on an interface (or an interface
method), but this works only as you would expect it to if you use interface-based
proxies. The fact that Java annotations are not inherited from interfaces means that,
if you use class-based proxies (`proxy-target-class="true"`) or the weaving-based
aspect (`mode="aspectj"`), the transaction settings are not recognized by the proxying
and weaving infrastructure, and the object is not wrapped in a transactional proxy.
NOTE: In proxy mode (which is the default), only external method calls coming in through
the proxy are intercepted. This means that self-invocation (in effect, a method within
the target object calling another method of the target object) does not lead to an actual
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
the proxy must be fully initialized to provide the expected behavior, so you should not
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
method.
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
expect self-invocations to be wrapped with transactions as well. In this case, there is
no proxy in the first place. Instead, the target class is woven (that is, its byte code
is modified) to support `@Transactional` runtime behavior on any kind of method.
[[tx-annotation-driven-settings]]
.Annotation driven transaction settings
|===
| XML Attribute| Annotation Attribute| Default| Description
| `transaction-manager`
| N/A (see {api-spring-framework}/transaction/annotation/TransactionManagementConfigurer.html[`TransactionManagementConfigurer`] javadoc)
| `transactionManager`
| Name of the transaction manager to use. Required only if the name of the transaction
manager is not `transactionManager`, as in the preceding example.
| `mode`
| `mode`
| `proxy`
| The default mode (`proxy`) processes annotated beans to be proxied by using Spring's AOP
framework (following proxy semantics, as discussed earlier, applying to method calls
coming in through the proxy only). The alternative mode (`aspectj`) instead weaves the
affected classes with Spring's AspectJ transaction aspect, modifying the target class
byte code to apply to any kind of method call. AspectJ weaving requires
`spring-aspects.jar` in the classpath as well as having load-time weaving (or compile-time
weaving) enabled. (See <<core.adoc#aop-aj-ltw-spring, Spring configuration>>
for details on how to set up load-time weaving.)
| `proxy-target-class`
| `proxyTargetClass`
| `false`
| Applies to `proxy` mode only. Controls what type of transactional proxies are created
for classes annotated with the `@Transactional` annotation. If the
`proxy-target-class` attribute is set to `true`, class-based proxies are created.
If `proxy-target-class` is `false` or if the attribute is omitted, then standard JDK
interface-based proxies are created. (See <<core.adoc#aop-proxying, Proxying Mechanisms>>
for a detailed examination of the different proxy types.)
| `order`
| `order`
| `Ordered.LOWEST_PRECEDENCE`
| Defines the order of the transaction advice that is applied to beans annotated with
`@Transactional`. (For more information about the rules related to ordering of AOP
advice, see <<core.adoc#aop-ataspectj-advice-ordering, Advice Ordering>>.)
No specified ordering means that the AOP subsystem determines the order of the advice.
|===
NOTE: The default advice mode for processing `@Transactional` annotations is `proxy`,
which allows for interception of calls through the proxy only. Local calls within the
same class cannot get intercepted that way. For a more advanced mode of interception,
consider switching to `aspectj` mode in combination with compile-time or load-time weaving.
NOTE: The `proxy-target-class` attribute controls what type of transactional proxies are
created for classes annotated with the `@Transactional` annotation. If
`proxy-target-class` is set to `true`, class-based proxies are created. If
`proxy-target-class` is `false` or if the attribute is omitted, standard JDK
interface-based proxies are created. (See <<core.adoc#aop-proxying, Proxying Mechanisms>>
for a discussion of the different proxy types.)
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` look for
`@Transactional` only on beans in the same application context in which they are defined.
This means that, if you put annotation-driven configuration in a `WebApplicationContext`
for a `DispatcherServlet`, it checks for `@Transactional` beans only in your controllers
and not in your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
The most derived location takes precedence when evaluating the transactional settings
for a method. In the case of the following example, the `DefaultFooService` class is
annotated at the class level with the settings for a read-only transaction, but the
`@Transactional` annotation on the `updateFoo(Foo)` method in the same class takes
precedence over the transactional settings defined at the class level.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// ...
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// ...
}
}
----
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Transactional(readOnly = true)
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
override fun updateFoo(foo: Foo) {
// ...
}
}
----
[[transaction-declarative-attransactional-settings]]
== `@Transactional` Settings
The `@Transactional` annotation is metadata that specifies that an interface, class,
or method must have transactional semantics (for example, "start a brand new read-only
transaction when this method is invoked, suspending any existing transaction").
The default `@Transactional` settings are as follows:
* The propagation setting is `PROPAGATION_REQUIRED.`
* The isolation level is `ISOLATION_DEFAULT.`
* The transaction is read-write.
* The transaction timeout defaults to the default timeout of the underlying transaction
system, or to none if timeouts are not supported.
* Any `RuntimeException` or `Error` triggers rollback, and any checked `Exception` does
not.
You can change these default settings. The following table summarizes the various
properties of the `@Transactional` annotation:
[[tx-attransactional-properties]]
.@Transactional Settings
|===
| Property| Type| Description
| <<tx-multiple-tx-mgrs-with-attransactional,value>>
| `String`
| Optional qualifier that specifies the transaction manager to be used.
| `transactionManager`
| `String`
| Alias for `value`.
| `label`
| Array of `String` labels to add an expressive description to the transaction.
| Labels may be evaluated by transaction managers to associate implementation-specific behavior with the actual transaction.
| <<tx-propagation,propagation>>
| `enum`: `Propagation`
| Optional propagation setting.
| `isolation`
| `enum`: `Isolation`
| Optional isolation level. Applies only to propagation values of `REQUIRED` or `REQUIRES_NEW`.
| `timeout`
| `int` (in seconds of granularity)
| Optional transaction timeout. Applies only to propagation values of `REQUIRED` or `REQUIRES_NEW`.
| `timeoutString`
| `String` (in seconds of granularity)
| Alternative for specifying the `timeout` in seconds as a `String` value -- for example, as a placeholder.
| `readOnly`
| `boolean`
| Read-write versus read-only transaction. Only applicable to values of `REQUIRED` or `REQUIRES_NEW`.
| `rollbackFor`
| Array of `Class` objects, which must be derived from `Throwable.`
| Optional array of exception types that must cause rollback.
| `rollbackForClassName`
| Array of exception name patterns.
| Optional array of exception name patterns that must cause rollback.
| `noRollbackFor`
| Array of `Class` objects, which must be derived from `Throwable.`
| Optional array of exception types that must not cause rollback.
| `noRollbackForClassName`
| Array of exception name patterns.
| Optional array of exception name patterns that must not cause rollback.
|===
TIP: See <<transaction-declarative-rollback-rules, Rollback rules>> for further details
on rollback rule semantics, patterns, and warnings regarding possible unintentional
matches for pattern-based rollback rules.
Currently, you cannot have explicit control over the name of a transaction, where 'name'
means the transaction name that appears in a transaction monitor, if applicable
(for example, WebLogic's transaction monitor), and in logging output. For declarative
transactions, the transaction name is always the fully-qualified class name + `.`
+ the method name of the transactionally advised class. For example, if the
`handlePayment(..)` method of the `BusinessService` class started a transaction, the
name of the transaction would be: `com.example.BusinessService.handlePayment`.
[[tx-multiple-tx-mgrs-with-attransactional]]
== Multiple Transaction Managers with `@Transactional`
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` 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
----
public class TransactionalService {
@Transactional("order")
public void setSomething(String name) { ... }
@Transactional("account")
public void doSomething() { ... }
@Transactional("reactive-account")
public Mono<Void> doSomethingReactive() { ... }
}
----
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
class TransactionalService {
@Transactional("order")
fun setSomething(name: String) {
// ...
}
@Transactional("account")
fun doSomething() {
// ...
}
@Transactional("reactive-account")
fun doSomethingReactive(): Mono<Void> {
// ...
}
}
----
The following listing shows the bean declarations:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="account"/>
</bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
...
<qualifier value="reactive-account"/>
</bean>
----
In this case, the individual methods on `TransactionalService` run under separate
transaction managers, differentiated by the `order`, `account`, and `reactive-account`
qualifiers. The default `<tx:annotation-driven>` target bean name, `transactionManager`,
is still used if no specifically qualified `TransactionManager` bean is found.
[[tx-custom-attributes]]
== Custom Composed Annotations
If you find you repeatedly use the same attributes with `@Transactional` on many different
methods, <<core.adoc#beans-meta-annotations, Spring's meta-annotation support>> lets you
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"]
.Java
----
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "order", label = "causal-consistency")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "account", label = "retryable")
public @interface AccountTx {
}
----
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional(transactionManager = "order", label = ["causal-consistency"])
annotation class OrderTx
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional(transactionManager = "account", label = ["retryable"])
annotation class AccountTx
----
The preceding annotations let us write the example from the previous section as follows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TransactionalService {
@OrderTx
public void setSomething(String name) {
// ...
}
@AccountTx
public void doSomething() {
// ...
}
}
----
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
class TransactionalService {
@OrderTx
fun setSomething(name: String) {
// ...
}
@AccountTx
fun doSomething() {
// ...
}
}
----
In the preceding example, we used the syntax to define the transaction manager qualifier
and transactional labels, but we could also have included propagation behavior,
rollback rules, timeouts, and other features.

View File

@@ -0,0 +1,216 @@
[[transaction-declarative-applying-more-than-just-tx-advice]]
= Advising Transactional Operations
Suppose you want to run both transactional operations and some basic profiling advice.
How do you effect this in the context of `<tx:annotation-driven/>`?
When you invoke the `updateFoo(Foo)` method, you want to see the following actions:
* The configured profiling aspect starts.
* The transactional advice runs.
* The method on the advised object runs.
* The transaction commits.
* The profiling aspect reports the exact duration of the whole transactional method invocation.
NOTE: This chapter is not concerned with explaining AOP in any great detail (except as it
applies to transactions). See <<core.adoc#aop,AOP>> for detailed coverage of the AOP
configuration and AOP in general.
The following code shows the simple profiling aspect discussed earlier:
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;
public class SimpleProfiler implements Ordered {
private int order;
// allows us to control the ordering of advice
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
// this method is the around advice
public Object profile(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
clock.start(call.toShortString());
returnValue = call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}
----
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y
import org.aspectj.lang.ProceedingJoinPoint
import org.springframework.util.StopWatch
import org.springframework.core.Ordered
class SimpleProfiler : Ordered {
private var order: Int = 0
// allows us to control the ordering of advice
override fun getOrder(): Int {
return this.order
}
fun setOrder(order: Int) {
this.order = order
}
// this method is the around advice
fun profile(call: ProceedingJoinPoint): Any {
var returnValue: Any
val clock = StopWatch(javaClass.name)
try {
clock.start(call.toShortString())
returnValue = call.proceed()
} finally {
clock.stop()
println(clock.prettyPrint())
}
return returnValue
}
}
----
The ordering of advice
is controlled through the `Ordered` interface. For full details on advice ordering, see
<<core.adoc#aop-ataspectj-advice-ordering,Advice ordering>>.
The following configuration creates a `fooService` bean that has profiling and
transactional aspects applied to it in the desired order:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- this is the aspect -->
<bean id="profiler" class="x.y.SimpleProfiler">
<!-- run before the transactional advice (hence the lower order number) -->
<property name="order" value="1"/>
</bean>
<tx:annotation-driven transaction-manager="txManager" order="200"/>
<aop:config>
<!-- this advice runs around the transactional advice -->
<aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue"
expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</aop:config>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
----
You can configure any number
of additional aspects in similar fashion.
The following example creates the same setup as the previous two examples but uses the purely XML
declarative approach:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the profiling advice -->
<bean id="profiler" class="x.y.SimpleProfiler">
<!-- run before the transactional advice (hence the lower order number) -->
<property name="order" value="1"/>
</bean>
<aop:config>
<aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>
<!-- runs after the profiling advice (cf. the order attribute) -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" order="2"/>
<!-- order value is higher than the profiling aspect -->
<aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue"
expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- other <bean/> definitions such as a DataSource and a TransactionManager here -->
</beans>
----
The result of the preceding configuration is a `fooService` bean that has profiling and
transactional aspects applied to it in that order. If you want the profiling advice
to run after the transactional advice on the way in and before the
transactional advice on the way out, you can swap the value of the profiling
aspect bean's `order` property so that it is higher than the transactional advice's
order value.
You can configure additional aspects in similar fashion.

View File

@@ -0,0 +1,60 @@
[[transaction-declarative-aspectj]]
= Using `@Transactional` with AspectJ
You can also use the Spring Framework's `@Transactional` support outside of a Spring
container by means of an AspectJ aspect. To do so, first annotate your classes
(and optionally your classes' methods) with the `@Transactional` annotation,
and then link (weave) your application with the
`org.springframework.transaction.aspectj.AnnotationTransactionAspect` defined in the
`spring-aspects.jar` file. You must also configure the aspect with a transaction
manager. You can use the Spring Framework's IoC container to take care of
dependency-injecting the aspect. The simplest way to configure the transaction
management aspect is to use the `<tx:annotation-driven/>` element and specify the `mode`
attribute to `aspectj` as described in <<transaction-declarative-annotations>>. Because
we focus here on applications that run outside of a Spring container, we show
you how to do it programmatically.
NOTE: Prior to continuing, you may want to read <<transaction-declarative-annotations>> and
<<core.adoc#aop, AOP>> respectively.
The following example shows how to create a transaction manager and configure the
`AnnotationTransactionAspect` to use it:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// construct an appropriate transaction manager
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// construct an appropriate transaction manager
val txManager = DataSourceTransactionManager(getDataSource())
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().transactionManager = txManager
----
NOTE: When you use this aspect, you must annotate the implementation class (or the methods
within that class or both), not the interface (if any) that the class implements. AspectJ
follows Java's rule that annotations on interfaces are not inherited.
The `@Transactional` annotation on a class specifies the default transaction semantics
for the execution of any public method in the class.
The `@Transactional` annotation on a method within the class overrides the default
transaction semantics given by the class annotation (if present). You can annotate any method,
regardless of visibility.
To weave your applications with the `AnnotationTransactionAspect`, you must either build
your application with AspectJ (see the
https://www.eclipse.org/aspectj/doc/released/devguide/index.html[AspectJ Development
Guide]) or use load-time weaving. See <<core.adoc#aop-aj-ltw,Load-time weaving with
AspectJ in the Spring Framework>> for a discussion of load-time weaving with AspectJ.

View File

@@ -0,0 +1,114 @@
[[transaction-declarative-diff-tx]]
= Configuring Different Transactional Semantics for Different Beans
Consider the scenario where you have a number of service layer objects, and you want to
apply a totally different transactional configuration to each of them. You can do so
by defining distinct `<aop:advisor/>` elements with differing `pointcut` and
`advice-ref` attribute values.
As a point of comparison, first assume that all of your service layer classes are
defined in a root `x.y.service` package. To make all beans that are instances of classes
defined in that package (or in subpackages) and that have names ending in `Service` have
the default transactional configuration, you could write the following:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* x.y.service..*Service.*(..))"/>
<aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
</aop:config>
<!-- these two beans will be transactional... -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<bean id="barService" class="x.y.service.extras.SimpleBarService"/>
<!-- ... and these two beans won't -->
<bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) -->
<bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in 'Service') -->
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- other transaction infrastructure beans such as a TransactionManager omitted... -->
</beans>
----
The following example shows how to configure two distinct beans with totally different
transactional settings:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="defaultServiceOperation"
expression="execution(* x.y.service.*Service.*(..))"/>
<aop:pointcut id="noTxServiceOperation"
expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
</aop:config>
<!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- this bean will also be transactional, but with totally different transactional settings -->
<bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>
<tx:advice id="defaultTxAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="noTxAdvice">
<tx:attributes>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>
<!-- other transaction infrastructure beans such as a TransactionManager omitted... -->
</beans>
----

View File

@@ -0,0 +1,399 @@
[[transaction-declarative-first-example]]
= Example of Declarative Transaction Implementation
Consider the following interface and its attendant implementation. This example uses
`Foo` and `Bar` classes as placeholders so that you can concentrate on the transaction
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 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",chomp="-packages"]
.Java
----
// the service interface that we want to make transactional
package x.y.service;
public interface FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
// the service interface that we want to make transactional
package x.y.service
interface FooService {
fun getFoo(fooName: String): Foo
fun getFoo(fooName: String, barName: String): Foo
fun insertFoo(foo: Foo)
fun updateFoo(foo: Foo)
}
----
The following example shows an implementation of the preceding interface:
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y.service;
public class DefaultFooService implements FooService {
@Override
public Foo getFoo(String fooName) {
// ...
}
@Override
public Foo getFoo(String fooName, String barName) {
// ...
}
@Override
public void insertFoo(Foo foo) {
// ...
}
@Override
public void updateFoo(Foo foo) {
// ...
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y.service
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
override fun getFoo(fooName: String, barName: String): Foo {
// ...
}
override fun insertFoo(foo: Foo) {
// ...
}
override fun updateFoo(foo: Foo) {
// ...
}
}
----
Assume that the first two methods of the `FooService` interface, `getFoo(String)` and
`getFoo(String, String)`, must run in the context of a transaction with read-only
semantics and that the other methods, `insertFoo(Foo)` and `updateFoo(Foo)`, must
run in the context of a transaction with read-write semantics. The following
configuration is explained in detail in the next few paragraphs:
[source,xml,indent=0,subs="verbatim"]
----
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!-- don't forget the DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- similarly, don't forget the TransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
----
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 `<tx:advice/>` definition. The `<tx:advice/>` definition reads as "all methods
starting with `get` are to run in the context of a read-only transaction, and all
other methods are to run with the default transaction semantics". The
`transaction-manager` attribute of the `<tx:advice/>` tag is set to the name of the
`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
(`<tx:advice/>`) 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.
The `<aop:config/>` definition ensures that the transactional advice defined by the
`txAdvice` bean runs at the appropriate points in the program. First, you define a
pointcut that matches the execution of any operation defined in the `FooService` interface
(`fooServiceOperation`). Then you associate the pointcut with the `txAdvice` by using an
advisor. The result indicates that, at the execution of a `fooServiceOperation`,
the advice defined by `txAdvice` is run.
The expression defined within the `<aop:pointcut/>` element is an AspectJ pointcut
expression. See <<core.adoc#aop, the AOP section>> for more details on pointcut
expressions in Spring.
A common requirement is to make an entire service layer transactional. The best way to
do this is to change the pointcut expression to match any operation in your
service layer. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>
----
NOTE: In the preceding example, it is assumed that all your service interfaces are defined
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,
"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
the transactional advice so that, when an appropriate method is invoked on the proxy,
a transaction is started, suspended, marked as read-only, and so on, depending on the
transaction configuration associated with that method. Consider the following program
that test drives the configuration shown earlier:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
FooService fooService = ctx.getBean(FooService.class);
fooService.insertFoo(new Foo());
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.beans.factory.getBean
fun main() {
val ctx = ClassPathXmlApplicationContext("context.xml")
val fooService = ctx.getBean<FooService>("fooService")
fooService.insertFoo(Foo())
}
----
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):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- the Spring container is starting up... -->
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 1 specific interceptors
<!-- the DefaultFooService is actually proxied -->
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]
<!-- ... the insertFoo(..) method is now being invoked on the proxy -->
[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo
<!-- the transactional advice kicks in here... -->
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction
<!-- the insertFoo(..) method from DefaultFooService throws an exception... -->
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException]
<!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource
Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
<!-- AOP infrastructure stack trace elements removed for clarity -->
at $Proxy0.insertFoo(Unknown Source)
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",chomp="-packages"]
.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",chomp="-packages"]
.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",chomp="-packages"]
.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",chomp="-packages"]
.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.

View File

@@ -0,0 +1,170 @@
[[transaction-declarative-rolling-back]]
= Rolling Back a Declarative Transaction
The previous section outlined the basics of how to specify transactional settings for
classes, typically service layer classes, declaratively in your application. This section
describes how you can control the rollback of transactions in a simple, declarative
fashion in XML configuration. For details on controlling rollback semantics declaratively
with the `@Transactional` annotation, see
<<transaction-declarative-attransactional-settings>>.
The recommended way to indicate to the Spring Framework's transaction infrastructure
that a transaction's work is to be rolled back is to throw an `Exception` from code that
is currently executing in the context of a transaction. The Spring Framework's
transaction infrastructure code catches any unhandled `Exception` as it bubbles up
the call stack and makes a determination whether to mark the transaction for rollback.
In its default configuration, the Spring Framework's transaction infrastructure code
marks a transaction for rollback only in the case of runtime, unchecked exceptions.
That is, when the thrown exception is an instance or subclass of `RuntimeException`.
(`Error` instances also, by default, result in a rollback).
As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
Here's an example of how to use Vavr's Try with a transactional method:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Transactional
public Try<String> myTransactionalMethod() {
// If myDataAccessOperation throws an exception, it will be caught by the
// Try instance created with Try.of() and wrapped inside the Failure class
// which can be checked using the isFailure() method on the Try instance.
return Try.of(delegate::myDataAccessOperation);
}
----
Checked exceptions that are thrown from a transactional method do not result in a rollback
in the default configuration. You can configure exactly which `Exception` types mark a
transaction for rollback, including checked exceptions by specifying _rollback rules_.
.Rollback rules
[[transaction-declarative-rollback-rules]]
[NOTE]
====
Rollback rules determine if a transaction should be rolled back when a given exception is
thrown, and the rules are based on exception types or exception patterns.
Rollback rules may be configured in XML via the `rollback-for` and `no-rollback-for`
attributes, which allow rules to be defined as patterns. When using
<<transaction-declarative-attransactional-settings,`@Transactional`>>, rollback rules may
be configured via the `rollbackFor`/`noRollbackFor` and
`rollbackForClassName`/`noRollbackForClassName` attributes, which allow rules to be
defined based on exception types or patterns, respectively.
When a rollback rule is defined with an exception type, that type will be used to match
against the type of a thrown exception and its super types, providing type safety and
avoiding any unintentional matches that may occur when using a pattern. For example, a
value of `jakarta.servlet.ServletException.class` will only match thrown exceptions of
type `jakarta.servlet.ServletException` and its subclasses.
When a rollback rule is defined with an exception pattern, the pattern can be a fully
qualified class name or a substring of a fully qualified class name for an exception type
(which must be a subclass of `Throwable`), with no wildcard support at present. For
example, a value of `"jakarta.servlet.ServletException"` or `"ServletException"` will
match `jakarta.servlet.ServletException` and its subclasses.
[WARNING]
=====
You must carefully consider how specific a pattern is and whether to include package
information (which isn't mandatory). For example, `"Exception"` will match nearly
anything and will probably hide other rules. `"java.lang.Exception"` would be correct if
`"Exception"` were meant to define a rule for all checked exceptions. With more unique
exception names such as `"BaseBusinessException"` there is likely no need to use the
fully qualified class name for the exception pattern.
Furthermore, pattern-based rollback rules may result in unintentional matches for
similarly named exceptions and nested classes. This is due to the fact that a thrown
exception is considered to be a match for a given pattern-based rollback rule if the name
of the thrown exception contains the exception pattern configured for the rollback rule.
For example, given a rule configured to match on `"com.example.CustomException"`, that
rule will match against an exception named `com.example.CustomExceptionV2` (an exception
in the same package as `CustomException` but with an additional suffix) or an exception
named `com.example.CustomException$AnotherException` (an exception declared as a nested
class in `CustomException`).
=====
====
The following XML snippet demonstrates how to configure rollback for a checked,
application-specific `Exception` type by supplying an _exception pattern_ via the
`rollback-for` attribute:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
----
If you do not want a transaction rolled back when an exception is thrown, you can also
specify 'no rollback' rules. The following example tells the Spring Framework's
transaction infrastructure to commit the attendant transaction even in the face of an
unhandled `InstrumentNotFoundException`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
----
When the Spring Framework's transaction infrastructure catches an exception and consults
the configured rollback rules to determine whether to mark the transaction for rollback,
the strongest matching rule wins. So, in the case of the following configuration, any
exception other than an `InstrumentNotFoundException` results in a rollback of the
attendant transaction:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>
----
You can also indicate a required rollback programmatically. Although simple, this process
is quite invasive and tightly couples your code to the Spring Framework's transaction
infrastructure. The following example shows how to programmatically indicate a required
rollback:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun resolvePosition() {
try {
// some business logic...
} catch (ex: NoProductInStockException) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
----
You are strongly encouraged to use the declarative approach to rollback, if at all
possible. Programmatic rollback is available should you absolutely need it, but its
usage flies in the face of achieving a clean POJO-based architecture.

View File

@@ -0,0 +1,47 @@
[[tx-decl-explained]]
= Understanding the Spring Framework's Declarative Transaction Implementation
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 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
<<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 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 <<core.adoc#aop, the AOP section>>.
Spring Framework'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.
[NOTE]
====
`@Transactional` commonly works with thread-bound transactions managed by
`PlatformTransactionManager`, exposing a transaction to all data access operations within
the current execution thread. Note: This does _not_ propagate to newly started threads
within the method.
A reactive transaction managed by `ReactiveTransactionManager` uses the Reactor context
instead of thread-local attributes. As a consequence, all participating data access
operations need to execute within the same Reactor context in the same reactive pipeline.
====
The following image shows a conceptual view of calling a method on a transactional proxy:
image::tx.png[]

View File

@@ -0,0 +1,72 @@
[[tx-propagation]]
= Transaction Propagation
This section describes some semantics of transaction propagation in Spring. Note
that this section is not a proper introduction to transaction propagation. Rather, it
details some of the semantics regarding transaction propagation in Spring.
In Spring-managed transactions, be aware of the difference between physical and
logical transactions, and how the propagation setting applies to this difference.
[[tx-propagation-required]]
== Understanding `PROPAGATION_REQUIRED`
image::tx_prop_required.png[]
`PROPAGATION_REQUIRED` enforces a physical transaction, either locally for the current
scope if no transaction exists yet or participating in an existing 'outer' transaction
defined for a larger scope. This is a fine default in common call stack arrangements
within the same thread (for example, a service facade that delegates to several repository methods
where all the underlying resources have to participate in the service-level transaction).
NOTE: By default, a participating transaction joins the characteristics of the outer scope,
silently ignoring the local isolation level, timeout value, or read-only flag (if any).
Consider switching the `validateExistingTransactions` flag to `true` on your transaction
manager if you want isolation level declarations to be rejected when participating in
an existing transaction with a different isolation level. This non-lenient mode also
rejects read-only mismatches (that is, an inner read-write transaction that tries to participate
in a read-only outer scope).
When the propagation setting is `PROPAGATION_REQUIRED`, a logical transaction scope
is created for each method upon which the setting is applied. Each such logical
transaction scope can determine rollback-only status individually, with an outer
transaction scope being logically independent from the inner transaction scope.
In the case of standard `PROPAGATION_REQUIRED` behavior, all these scopes are
mapped to the same physical transaction. So a rollback-only marker set in the inner
transaction scope does affect the outer transaction's chance to actually commit.
However, in the case where an inner transaction scope sets the rollback-only marker, the
outer transaction has not decided on the rollback itself, so the rollback (silently
triggered by the inner transaction scope) is unexpected. A corresponding
`UnexpectedRollbackException` is thrown at that point. This is expected behavior so
that the caller of a transaction can never be misled to assume that a commit was
performed when it really was not. So, if an inner transaction (of which the outer caller
is not aware) silently marks a transaction as rollback-only, the outer caller still
calls commit. The outer caller needs to receive an `UnexpectedRollbackException` to
indicate clearly that a rollback was performed instead.
[[tx-propagation-requires_new]]
== Understanding `PROPAGATION_REQUIRES_NEW`
image::tx_prop_requires_new.png[]
`PROPAGATION_REQUIRES_NEW`, in contrast to `PROPAGATION_REQUIRED`, always uses an
independent physical transaction for each affected transaction scope, never
participating in an existing transaction for an outer scope. In such an arrangement,
the underlying resource transactions are different and, hence, can commit or roll back
independently, with an outer transaction not affected by an inner transaction's rollback
status and with an inner transaction's locks released immediately after its completion.
Such an independent inner transaction can also declare its own isolation level, timeout,
and read-only settings and not inherit an outer transaction's characteristics.
[[tx-propagation-nested]]
== Understanding `PROPAGATION_NESTED`
`PROPAGATION_NESTED` uses a single physical transaction with multiple savepoints
that it can roll back to. Such partial rollbacks let an inner transaction scope
trigger a rollback for its scope, with the outer transaction being able to continue
the physical transaction despite some operations having been rolled back. This setting
is typically mapped onto JDBC savepoints, so it works only with JDBC resource
transactions. See Spring's {api-spring-framework}/jdbc/datasource/DataSourceTransactionManager.html[`DataSourceTransactionManager`].

View File

@@ -0,0 +1,63 @@
[[transaction-declarative-txadvice-settings]]
= <tx:advice/> Settings
This section summarizes the various transactional settings that you can specify by using
the `<tx:advice/>` tag. The default `<tx:advice/>` settings are:
* The <<tx-propagation, propagation setting>> is `REQUIRED.`
* The isolation level is `DEFAULT.`
* The transaction is read-write.
* The transaction timeout defaults to the default timeout of the underlying transaction
system or none if timeouts are not supported.
* Any `RuntimeException` triggers rollback, and any checked `Exception` does not.
You can change these default settings. The following table summarizes the various attributes of the `<tx:method/>` tags
that are nested within `<tx:advice/>` and `<tx:attributes/>` tags:
[[tx-method-settings]]
.<tx:method/> settings
|===
| Attribute| Required?| Default| Description
| `name`
| Yes
|
| Method names with which the transaction attributes are to be associated. The
wildcard ({asterisk}) character can be used to associate the same transaction attribute
settings with a number of methods (for example, `get*`, `handle*`, `on*Event`, and so
forth).
| `propagation`
| No
| `REQUIRED`
| Transaction propagation behavior.
| `isolation`
| No
| `DEFAULT`
| Transaction isolation level. Only applicable to propagation settings of `REQUIRED` or `REQUIRES_NEW`.
| `timeout`
| No
| -1
| Transaction timeout (seconds). Only applicable to propagation `REQUIRED` or `REQUIRES_NEW`.
| `read-only`
| No
| false
| Read-write versus read-only transaction. Applies only to `REQUIRED` or `REQUIRES_NEW`.
| `rollback-for`
| No
|
| Comma-delimited list of `Exception` instances that trigger rollback. For example,
`com.foo.MyBusinessException,ServletException`.
| `no-rollback-for`
| No
|
| Comma-delimited list of `Exception` instances that do not trigger rollback. For example,
`com.foo.MyBusinessException,ServletException`.
|===