|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Framework 7.0.9! |
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.
The standard jakarta.transaction.Transactional annotation is also supported as
a drop-in replacement for 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:
-
Java
-
Kotlin
// 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) {
// ...
}
}
// 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 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
javadoc
for full details.
The following example shows the configuration needed to enable annotation-driven transaction management:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public FooService fooService() {
return new DefaultFooService();
}
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@EnableTransactionManagement
class AppConfig {
@Bean
fun fooService(): FooService {
return DefaultFooService()
}
@Bean
fun txManager(dataSource: DataSource): PlatformTransactionManager {
return DataSourceTransactionManager(dataSource)
}
}
<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"/>
<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>
In programmatic configuration, the @EnableTransactionManagement annotation uses any
TransactionManager bean in the context. In XML configuration, 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 has any other name, you have to use the
transaction-manager attribute explicitly, as in the preceding example.
|
Reactive transactional methods use reactive return types in contrast to imperative programming arrangements as the following listing shows:
-
Java
-
Kotlin
// 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) {
// ...
}
}
// 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
Cancel Signals
section under "Using the TransactionalOperator" for more details.
When @Transactional is combined with @Retryable, the retry advice is applied
outermost, so each retry attempt runs in its own transaction. See
Combining @Retryable with @Transactional
for details.
|
|
Method visibility and
@Transactional in proxy modeThe If you prefer consistent treatment of method visibility across the different kinds of
proxies (which was the default up until 5.3), consider specifying
The Spring TestContext Framework supports non-private |
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 corresponding
runtime infrastructure which uses that metadata to configure the appropriate beans with
transactional behavior. In the preceding examples that use programmatic configuration,
the @EnableTransactionManagement annotation switches on actual transaction management
at runtime. Whereas, in the preceding example that uses XML configuration, the
<tx:annotation-driven/> element switches on actual transaction management at runtime.
The Spring team recommends that you annotate methods of concrete classes with the
@Transactional annotation, rather than relying on annotated methods in interfaces,
even if the latter does work for interface-based and target-class proxies as of 5.0.
Since Java annotations are not inherited from interfaces, interface-declared annotations
are still not recognized by the weaving infrastructure when using AspectJ mode, so the
aspect does not get applied. As a consequence, your transaction annotations may be
silently ignored: Your code might appear to "work" until you test a rollback scenario.
|
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.
| XML Attribute | Annotation Attribute | Default | Description |
|---|---|---|---|
|
N/A (see |
|
Name of the transaction manager to use. Required only if the name of the transaction
manager is not |
|
|
|
The default mode ( |
|
|
|
Applies to |
|
|
|
Defines the order of the transaction advice that is applied to beans annotated with
|
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.
|