Wednesday, November 28, 2012

Don’t let tx:annotation-driven get you


When we start developing a new application, we typically have a single data source that we define and all of our business logic revolves around getting data back and forth the data source. As the application matures, we often run into scenarios where we need to depend on jars given my others or we give out our jars to others for them to use. With Spring involved, the context also becomes part of the contract between the two sub-systems. Here is where tx:annotation-driven can get you. Typically, we define a PlatformTransactionManager of org.springframework.jdbc.datasource.DataSourceTransactionManager and bind it to the datasource that it should manage.
    <bean id="testerDataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
        <property name="url" value="${tester.datasource.url}" />
        <property name="username" value="${tester.datasource.username}" />
        <property name="password" value="${tester.datasource.password}" />
    </bean>
    <bean id="testerTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="testerDataSource" />
    </bean>
Next, to achieve declarative transactional functionality on specific methods in our service, we can use tx:advice in conjunction with aop:pointcut to indicate the advice that will act on the methods matching the pointcut. However, I prefer defining annotations on the service method itself to effect this declaratively. The upside of this, in my opinion, is that the reader of the code is clearly made aware of what are the transactional boundaries the service method is going to honor.
    @Transactional
    public void getReferenceData() {
        referenceDataDAO.getReferenceData();
    }
From Spring’s manual:
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.
To get @Transactional to work its magic, i.e. create a proxy which does the heavy lifting of beginning the transaction, delegating the call to the actual class, and either committing or rolling back the transaction based on the result of the call, we need to inform Spring to scan all beans that have this annotation on it. We inform Spring using the “tx:annotation-driven” directive. It is a namespace handler which scans all beans in the Spring container to proxy all methods that have been annotated with @Transactional. The @Transactional can also be placed at the class level, when it is applicable to all methods in the class. So this namespace handler will by default look for a bean named ‘transactionManager’ of class PlatformTransactionManager. If your transaction manager bean happens to be named any differently, you have to state it explicitly. Another note of caution from Spring:
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@Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (using mode=”aspectj”) if you need to annotate non-public methods.
Using the “proxy-target-classes” clause, we are able to choose whether we create interface-based JDK proxies or CGLIB based proxies. The former will only work if the class you are proxying is implementing an interface whereas CGLIB-based ones work with either one. OK, so far so good, how is that tx:annotation-driven is going to get me? Just when it is all so comfortable, there is another data source that now comes in the context, either because you imported someone else’s jar or because you started talking to a new data source, tx:annotation-driven will try to hijack any @Transactional annotations intended for those data sources as well and ask the existing transaction manager to try and manage it. All that the transaction manager does is hold a connection from the data source it had been configured with in a threadlocal. When trying to manage a hijacked data source the threadlocal that it has kept with it is non-consequential in rolling back the transaction if required. The worst part is that there is no error that is thrown, just that the effect will not happen.
On a related note, because the connection is held in a threadlocal, if you are spawning threads or delegating control to other threads from a  method that is annotated with @Transactional, all bets are off and you might end up with infinite waits or deadlocks.
So, what is the recommended solution? Spring suggests that we qualify your transaction managers and rather than giving a bare @Transactional, specify the transaction manager (via qualifier) that you intend to use. In addition to this, one can also choose to avoid specifying the transaction manager when specifying the tx:annotation-driven which will force every user of @Transactional to specify the transaction manager it wishes to use (otherwise the container throws an exception saying that it didn’t find a bean with the name ‘transactionManager’). While this is more work, I think it is worth the clarity it provides and makes it explicit for users of the service method in other contexts.
References:
1. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction-declarative
2. Test bed available at https://github.com/kilokahn/spring-testers/tree/master/spring-tx-annotation-tester

No comments:

Post a Comment