13.5.6Using @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 standardjavax.transaction.Transactional annotation is also supported as a drop-in replacement to Spring’s own annotation. Please refer to JTA 1.2 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:
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
When the above POJO is defined as a bean in a Spring IoC container, the bean instance can be made transactional by adding merely_one_line of XML configuration:
<!-- 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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://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 -->
<tx:annotation-driven transaction-manager="txManager"/><!-- a PlatformTransactionManager is still required -->
<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>
You can omit thetransaction-manager attribute in the`tag if the bean name of the PlatformTransactionManagerthat you want to wire in has the name transactionManager. If the PlatformTransactionManagerbean that you want to dependency-inject has any other name, then you have to use the transaction-manager`attribute explicitly, as in the preceding example. |
The@EnableTransactionManagement annotation provides equivalent support if you are using Java based configuration. Simply add the annotation to a@Configuration class. See the javadocs for full details. |
---|
Method visibility and @Transactional
When using proxies, 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. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
You can place the@Transactional
annotation before an interface definition, a method on an interface, a class definition, or a_public_method on a class. However, the mere presence of the@Transactional
annotation is not enough to activate the transactional behavior. The@Transactional
annotation is simply 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``element_switches on_the transactional behavior.
Spring recommends that you only annotate 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 are using interface-based proxies. The fact that Java annotations arenot inherited from interfaces_means that if you are using class-based proxies (proxy-target-class="true" ) or the weaving-based aspect (mode="aspectj" ), then the transaction settings are not recognized by the proxying and weaving infrastructure, and the object will not be wrapped in a transactional proxy, which would be decidedly_bad. |
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, will 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 behaviour so you should not rely on this feature in your initialization code, i.e.@PostConstruct . |
Consider the use of AspectJ mode (see mode attribute in table below) if you expect self-invocations to be wrapped with transactions as well. In this case, there will not be a proxy in the first place; instead, the target class will be weaved (that is, its byte code will be modified) in order to turn@Transactional
into runtime behavior on any kind of method.
Table13.2.Annotation driven transaction settings
XML Attribute | Annotation Attribute | Default | Description |
---|---|---|---|
transaction-manager |
N/A (SeeTransactionManagementConfigurer javadocs) |
transactionManager | Name of transaction manager to use. Only required if the name of the transaction manager is nottransactionManager , as in the example above. |
mode |
mode |
proxy | The default mode "proxy" processes annotated beans to be proxied using Spring’s AOP framework (following proxy semantics, as discussed above, 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 load-time weaving (or compile-time weaving) enabled. (Seethe section called “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 theproxy-target-class attribute is set totrue , then class-based proxies are created. Ifproxy-target-class isfalse or if the attribute is omitted, then standard JDK interface-based proxies are created. (SeeSection7.6, “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, seethe section called “Advice ordering”.) No specified ordering means that the AOP subsystem determines the order of the advice. |
Theproxy-target-class attribute controls what type of transactional proxies are created for classes annotated with the@Transactional annotation. Ifproxy-target-class is set totrue , class-based proxies are created. Ifproxy-target-class isfalse or if the attribute is omitted, standard JDK interface-based proxies are created. (SeeSection7.6, “Proxying mechanisms”for a discussion of the different proxy types.) |
@EnableTransactionManagement and`only looks for @Transactionalon beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContextfor a DispatcherServlet, it only checks for @Transactional`beans in your controllers, and not your services. SeeSection18.2, “The DispatcherServlet”for more information. |
The most derived location takes precedence when evaluating the transactional settings for a method. In the case of the following example, theDefaultFooService
class is annotated at the class level with the settings for a read-only transaction, but the@Transactional
annotation on theupdateFoo(Foo)
method in the same class takes precedence over the transactional settings defined at the class level.
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
@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:
Propagation setting is
PROPAGATION_REQUIRED.
Isolation level is
ISOLATION_DEFAULT.
Transaction is read/write.
Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
Any
RuntimeException
triggers rollback, and any checkedException
does not.
These default settings can be changed; the various properties of the@Transactional
annotation are summarized in the following table:
Table13.3.@Transactional Settings
Property | Type | Description |
---|---|---|
value | String | Optional qualifier specifying the transaction manager to be used. |
propagation | enum:Propagation |
Optional propagation setting. |
isolation |
enum:Isolation |
Optional isolation level. |
readOnly |
boolean | Read/write vs. read-only transaction |
timeout |
int (in seconds granularity) | Transaction timeout. |
rollbackFor |
Array ofClass objects, which must be derived fromThrowable. |
Optional array of exception classes that_must_cause rollback. |
rollbackForClassName |
Array of class names. Classes must be derived fromThrowable. |
Optional array of names of exception classes that_must_cause rollback. |
noRollbackFor |
Array ofClass objects, which must be derived fromThrowable. |
Optional array of exception classes that_must not_cause rollback. |
noRollbackForClassName |
Array ofString class names, which must be derived fromThrowable. |
Optional array of names of exception classes that_must not_cause rollback. |
Currently you cannot have explicit control over the name of a transaction, where 'name' means the transaction name that will be shown 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 + "." + method name of the transactionally-advised class. For example, if thehandlePayment(..)
method of theBusinessService
class started a transaction, the name of the transaction would be:com.foo.BusinessService.handlePayment
.
Multiple Transaction Managers with @Transactional
Most Spring applications only need a single transaction manager, but there may be situations where you want multiple independent transaction managers in a single application. The value attribute of the@Transactional
annotation can be used to optionally specify the identity of thePlatformTransactionManager
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, the following Java code
public class TransactionalService {
@Transactional("order")
public void setSomething(String name) { ... }
@Transactional("account")
public void doSomething() { ... }
}
could be combined with the following transaction manager bean declarations in the application context.
<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>
In this case, the two methods onTransactionalService
will run under separate transaction managers, differentiated by the "order" and "account" qualifiers. The default`target bean name
transactionManager`will still be used if no specifically qualified PlatformTransactionManager bean is found.
Custom shortcut annotations
If you find you are repeatedly using the same attributes with@Transactional
on many different methods, thenSpring’s meta-annotation supportallows you to define custom shortcut annotations for your specific use cases. For example, defining the following annotations
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}
allows us to write the example from the previous section as
public class TransactionalService {
@OrderTx
public void setSomething(String name) { ... }
@AccountTx
public void doSomething() { ... }
}
Here we have used the syntax to define the transaction manager qualifier, but could also have included propagation behavior, rollback rules, timeouts etc.