Sunday, March 28, 2010

Annotation-Based Transactions

Spring provides several different approaches to transaction management:
  • Programmatic: You can write your transactions using Java source code directly. Normally you wouldn't do this unless you needed fine-grained control over some particular transaction.
  • Declarative: You can declare transaction definitions in either a centralized way using XML or in a distributed way using annotations:
    • XML-based: You can configure transactions in Spring's centralized application context file. Once again you have options:
      • Proxy-based: In this approach you wrap your service beans with transactional proxies. The proxy and the service bean both implement the same Java interface so the application can't tell them apart. This is a fairly verbose approach with respect to the actual context configuration though there are techniques you can use to streamline the configuration.
      • AOP-based: Here you define AOP aspects to endow your service beans with transactional semantics. Not quite as verbose as the proxy-based approach.
    • Annotation-based: You can use Java 5 annotations to distribute transaction configuration across Java classes.
With Spring, you typically apply transaction annotations either to Java interfaces for service beans, or else to the service beans themselves. If you apply the annotations to the interfaces then the transaction definition holds true for any classes implementing those interfaces. We'll do that here, but remember that you can apply the annotations to the service beans themselves as well.
Here's an interface for a simple article service. This service allows the application to get an article by ID, and also to post a comment for a given article.
The first thing to notice is again just how ridiculously simple this is. I probably don't need to explain it but I will anyway just to be safe. The @Transactional annotation we define on the interface is setting default values for the various methods that form the interface. So we are saying here that unless the method itself specifies otherwise, we want Propagation.REQUIRED for the propagation behavior, we want the default isolation level, and we want to flag methods as read-only.
The only other piece to mention is that I've overridden the readOnly setting for the postComment method since that's obviously a write method.
If it could be any simpler I'm not sure how. Now let's look at the Spring configuration file.

That's it—really! Make sure you include both the aop and tx namespaces and schema locations since that's where the magic lives. You have to wire your beans up just like you normally would. And you have to provide a transaction manager of one kind or another. I'm using the Hibernate 3 HibernateTransactionManager but use whatever you want. If you give your transaction manager the id transactionManager, then you can take advantage of a little Spring convention-over-configuration: <tx:annotation-driven> will pick that up automatically. (And if you want to call your transaction manager something else, that's fine too. Just reference that ID using the transaction-manager attribute of the <tx:annotation-driven> element.)
Oh, the <tx:annotation-driven> element just tells Spring that you want it to pick the transaction definitions up from the Java annotations. I'm not sure how it works behind the scenes, but I think Spring will run through the beans in your context and create the necessary transaction aspects. Not positive about that—if you know the details, I'm interested—but in any case you end up with annotation-driven transactions. Nice!

No comments:

Post a Comment