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.Transactionalannotation 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@Transactionalannotation 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-managerattribute in the`tag if the bean name of thePlatformTransactionManagerthat you want to wire in has the nametransactionManager. If thePlatformTransactionManagerbean that you want to dependency-inject has any other name, then you have to use thetransaction-manager`attribute explicitly, as in the preceding example.
The@EnableTransactionManagementannotation provides equivalent support if you are using Java based configuration. Simply add the annotation to a@Configurationclass. See the javadocs for full details.

Method visibility and @Transactional

When using proxies, you should apply the@Transactionalannotation only to methods with_public_visibility. If you do annotate protected, private or package-visible methods with the@Transactionalannotation, 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@Transactionalannotation 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@Transactionalannotation is not enough to activate the transactional behavior. The@Transactionalannotation 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@Transactionalannotation, as opposed to annotating interfaces. You certainly can place the@Transactionalannotation 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@Transactionalinto runtime behavior on any kind of method.

Table13.2.Annotation driven transaction settings

XML Attribute Annotation Attribute Default Description
transaction-manager N/A (SeeTransactionManagementConfigurerjavadocs) 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@Transactionalannotation. If theproxy-target-classattribute is set totrue, then class-based proxies are created. Ifproxy-target-classisfalseor 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-classattribute controls what type of transactional proxies are created for classes annotated with the@Transactionalannotation. Ifproxy-target-classis set totrue, class-based proxies are created. Ifproxy-target-classisfalseor if the attribute is omitted, standard JDK interface-based proxies are created. (SeeSection7.6, “Proxying mechanisms”for a discussion of the different proxy types.)
@EnableTransactionManagementand`only looks for@Transactionalon beans in the same application context they are defined in. This means that, if you put annotation driven configuration in aWebApplicationContextfor aDispatcherServlet, 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, theDefaultFooServiceclass is annotated at the class level with the settings for a read-only transaction, but the@Transactionalannotation 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@Transactionalannotation 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@Transactionalsettings are as follows:

  • Propagation setting isPROPAGATION_REQUIRED.

  • Isolation level isISOLATION_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.

  • AnyRuntimeExceptiontriggers rollback, and any checkedExceptiondoes not.

These default settings can be changed; the various properties of the@Transactionalannotation 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 ofClassobjects, 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 ofClassobjects, which must be derived fromThrowable. Optional array of exception classes that_must not_cause rollback.
noRollbackForClassName Array ofStringclass 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 theBusinessServiceclass 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@Transactionalannotation can be used to optionally specify the identity of thePlatformTransactionManagerto 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 onTransactionalServicewill run under separate transaction managers, differentiated by the "order" and "account" qualifiers. The default`target bean nametransactionManager`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@Transactionalon 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.

results matching ""

    No results matching ""