Spring 1.2’s Java 5 Based Transaction Annotations

Spring has from the beginning provided a powerful transaction abstraction. This abstraction provides several key benefits:

  • Allows you to specify transaction demaraction policies for your code, either declaratively or programatically.
  • Abstracts away the low-level transaction manager with a common transaction management abstraction, allowing for you to change TX strategies (for example, JTA (global) vs. local transactions) without impacting application code (or even your demaraction policies)

This abstraction is available while running in an AppServer, or as a standalone application.

While the programmatic API is easy to use, most people have preferred to use a declarative approach instead, since it means POJO business services can be wrapped without any code in those objects being aware of transactions at all. In the past, this has usually meant XML-based definitions of what objects and what methods in those objects should be wrapped, and with what transaciton semantics. The XML definitions are convenient to use, work in any environment, and a number of techniques exist for making them pretty compact.

However if you are willing to commit to a dependency on Java 5+ (JDK 1.5+), you will almost certainly want to consider using Spring’s support for transaction Annotations in JDK standard format, as the transaction attribute source. These Annotations were officially introduced in Spring 1.2, although they’d been available in essentially finished form in Spring’s CVS repository since late last summer.

Declaring transaction semantics directly in the Java source code puts the declarations much closer to the affected code, and there is generally not much danger of undue coupling, since typically, code that is deployed as transactional is always deployed that way. Additionally, all major IDEs now have support for Annotations, so using them is as easy as writing any Java code.

In the rest of this blog entry I’m going to describe the new Annotations. Note that some of this text is based upon similar content I wrote for the Spring documentation.

The Transactional Annotation

The org.springframework.transaction.annotation.Transactional Annotation is used to indicate that an interface, interface method, class, or class method should have transaction semantics.

@Transactional
public interface OrderService {
 
void createOrder(Order order);
List queryByCriteria(Order criteria);
...

Used in bare form, this Annotation specifies that an interface, class, or method must be transactional. Default transaction semantics are read/write, PROPAGATION_REQUIRED, ISOLATION_DEFAULT, TIMEOUT_DEFAULT, with rollback on a RuntimeException, but not Exception.

Optional properties of the annotation modify transaction settings.

Properties of the Transactional Annotation

Property Type Description
propagation enum: Propagation optional propagation setting (defaults to PROPAGATION_REQUIRED)
isolation enum: Isolation optional isolation level (defaults to ISOLATION_DEFAULT)
readOnly boolean read/write vs. read-only transaction (defaults to false, or read/write)
rollbackFor array of Class objects, must be derived from Throwable optional array of exception classes which should cause rollback. By default, checked exceptions do not roll back, unchecked (RuntimeException derived) roll back
rollbackForClassname array of String class names. Classes must be derived from Throwable optional array of names of exception classes which should cause rollback
noRollbackFor array of Class objects, must be derived from Throwable optional array of exception classes which should not cause rollback.
noRollbackForClassname array of String class names, must be derived from Throwable optional array of names of exception classes which should not rollback

The annotation may be placed before an interface definition, a method on an interface, a class definition, or a method on a class. It may exist on both an element of an interface, and a class which implements that interface. The most derived location takes precedence when evaluating the transaction semantics of a method.

Transactional annotation examples

In the following definition, we’re specifying that createOrder() should be transactional with default semantics:

public class OrderServiceImpl implements OrderService {
 
@Transactional
void createOrder(Order order);
public List queryByCriteria(Order criteria);
 
}

In the following example, the interface is annotated for read-only transactions, which will thus be the setting used for methods by default. The Annotation on the createOrder method overrides this, setting the transaction to read/write, and specifying that transactions should also (in addition to the defualt rollback rule for RuntimeException) rollback when the DuplicateOrderIdException (presumably a non-checked Exception) is thrown.

@Transactional(readOnly=true)
interface TestService {
 
@Transactional(readOnly=false,
rollbackFor=DuplicateOrderIdException.class)
void createOrder(Order order) throws DuplicateOrderIdException ;
 
List queryByCriteria(Order criteria);
}

Note that a class definition which implements this interface may still override these settings on its own class or method elements
.
Telling Spring to apply the Transactional annotation

By itself, adding instances of this Annotation to interface or class elements will not result in transactional wrapping of the implementation clases. Spring must still be told somehow to create transactional proxies around classes with these annotations.

The key is to take advantage of the org.springframework.transaction.annotation.AnnotationTransactionAttributeSource class, which is a Spring TransactionAttributeSource implementation which looks in class files for the Annotations we’ve been looking at, and from them produces standard Spring code>TransactionAttributes objects used by the transaction infrastructure.

Most Spring users doing transactional wrapping have been using TransactionProxyFactoryBean for the purpose, as detailed explicitly in the Spring reference manual. Let’s look at how to use TransactionAttributeSource with TransactionProxyFactoryBean. First, here’s a pure XML (and long-form) variant of a proxy definition:

 
PROPAGATION_REQUIRED,-MyCheckedException
PROPAGATION_REQUIRED
PROPAGATION_REQUIRED,readOnly

To convert to using Annotations, the TransactionAttributes property of the proxy factory bean, which specified transaction attributes in text form is replaced by the direct usage of the TransactionAttributeSource property, specifying an AnnotationTransactionAttributeSource.


Since the TransactionAttributeSource property does not need to change at all for each proxy instance, when using parent and child bean definitions to avoid code duplication, the property may just be set on the base, parent definition and forgotten, there is never a need to override it in the child since the attribute source will read the right settings from each class file.

Using AOP to ensure the Transactional annotation is applied

The previous example is still more work than would be ideal. There is in principle no need for XML for each proxy (to point to the target bean) when the annotations in the class files themselves can be used as an indication that a proxy needs to be created for the annotated classes.

A more AOP focused approach allows a small amount of boilerplate XML (used once only, not for each target bean) to automatically ensure that all classes managed by Spring, which contain Transactional annotations in them are automatically wrapped transactionally. Spring AOP is detailed in the reference manual, as well as a number of books, although the reality is that there is actually no need to understand how the following code actually works. If you are interested though, the key is the use of DefaultAdvisorAutoProxyCreator, a BeanPostProcessor. Because it is a bean post processor, it gets a chance to look at every bean that is created as it is created. If the bean contains the Transactional annotation, a transactional proxy is automatically created to wrap it.


A number of classes are involved here:

  • TransactionInterceptor: the AOP Advice, actually intercepts method call and wraps it with a transaction
  • TransactionAttributeSourceAdvisor: AOP Advisor (holds the TransactionInterceptor, which is the advice, and a pointcut (where to apply the advice), in the form of a TransactionAttributeSource)
  • AnnotationTransactionAttributeSource: TransactionAttributeSource implementation which provides transaction attributes read from class files
  • DefaultAdvisorAutoProxyCreator: looks for Advisors in the context, and automatically creates proxy objects which are the transactional wrappers

I hope that I’ve gotten you at least a little bit eager to try using the transaction annotations in your Spring-based applications. While annotations have the potential for misuse, and there’s already evidence of some of that happening, transaction annotations make a decent amount of sense in many situations. My feeling is that as more and more code starts running in a Java 5+ environment, and Java 5+ dependencies become acceptable, using annotations for transaction demarcation will probably be the prefered mechanism. They’re certainly a great way to reduce boilerplate XML, and move the information about what needs to be wrapped transactionally much closer to the actual code being wrapped, without any significant negatives.

 

10 responses


  1. One of the things I haven’t seen addressed in any of the Spring transactional advice is that the proxy approach, while valuable, isn’t easily applied when a class calls itself. This can be somewhat frustrating to someone developing a transactional service and decomposing functionality within that service.

  2. Colin Sampaleanu (blog author) says:


    Yes, it’s a fundamental limitation of the proxy approach to applying transaction semantics to user code, that because the original object still exists and is unmodified, if it tries to call back to itself normally, that internal method invocation will not get transactional wrapping.

    I should point out first that this is not a Spring-only concern. Any environment that uses proxies to apply transactions, such as EJB’s CMT, will have the same limitation.

    Having talked to a number of Spring users over the years, in practice this doesn’t actually hit you very often. It’s very rare for example that you call a non-transactional method on a service, and that needs to call an internal transactional method. Often when people have talked about hitting this as an issue, they said it was an indication that the class in question needed to be split up anyway. But people do hit it sometimes. One thing you see is a transactional method tha need to call a method marked as non-transactional.

    There are a few ways to get around this. One approach, which I don’t find incredibly clean, but does work, is to define a method like this in your service implementation or a parent class of it:

    MyInterfaceName getThis() {
     
      try {
        return (MyInterfaceName) AopContext.currentProxy();
      }
      catch (AspectException) {
        return this;
      }
    }
    

    What this method does is return the current proxy wrapping the service (note that when you create the proxy via ProxyFactoryBean or whatever, you must specifically tell Spring to expose the proxy (which happens via a ThreadLocal) to be available via AopContext.currentProxy(), by default this functionality is off).

    Then, you can do internal method invations through a getThis() function call, i.e.
    getThis().myMethod();

    Obviously this is fairly invasive, although at least all the Spring-specific stuff is localized in one method.

    Short of doing programmatic wrapping in every method, the other viable solution, and what you’ll probably see more of in the future, is the use of AspectJ to actually wrap services. Since AspectJ modifies the original bytecode, instead of using a proxy, even internal method calls will be intercepted.


  3. Another appraoch we have used to solve this problem is to inject the proxy into the target object and invoke the other methods on the proxy.

    public class Service
    {
      private Service proxy;
     
      public void setProxy(Service proxy)
      {
         this.proxy = proxy;
      }
     
      public void someMethod()
      {
         proxy.anotherMethod();
      }
     
      public void anotherMethod()
      {
      }
    }
    
  4. Lucas Santos says:


    We use an approach similar to Kamal’s, but making Service a private static member class, and injecting it via a constructor. IMO the solution would be perfect if we could hide the injection entirely, say, in a private setter, so the whole scheme would be an implementation detail. However, Spring doesn’t invoke private setter methods.


  5. Sorry, I hadn’t meant to imply that it was a problem that was only exposed in Spring; I agree it’s true of other proxy approaches, but it’s with respect to applying transactions that I’ve found it the most annoying, and lately, I’ve been doing that with Spring.


  6. may I know how to inject via constructor,
    do you need to set up via the xml?
    I have got the below error when I do so
    org.springframework.beans.factory.FactoryBeanNotInitializedException: Error creating bean with name ‘BeanGalleryJob’: FactoryBean returned null object: probably not fully initialized (maybe due to circular bean reference)

    many thanks

  7. Colin Sampaleanu (blog author) says:


    Hy, I think your question is not even about the transaction annotations, right? Please post Spring support questions on the support forums (forum.springframework.org).


  8. [TRACKBACK]http://jroller.com/page/dgolla/?anchor=p_font_face_courier_a

  9. Dan says:


    I got blocked as a spammer, so I’ll try again without all of the code samples. =)

    This is a great tip, and something I haven’t seen detailed anywhere else. Thank you.

    Unfortunately, I can’t seem to get it to work. I’ve posted my details to the SpringForums: http://forum.springframework.org/showthread.php?t=27649

    If you have a chance, I would appreciate any thoughts that you have on my hang-ups. (Or pointers to any books that discuss this technique in more detail.)


  10. Welcome to our website for you World of Warcraft Gold,Wow Gold,Cheap World of Warcraft Gold,cheap wow gold,buy cheap wow gold,real wow gold,sell wow gold, … buy ffxi gil,
    Here wow gold of 1000 gold at $68.99-$80.99 ,World Of Warcraft Gold,buy wow gold,buy ffxi gil sell world of warcraft gold(wow gold),buy euro gold wow Cheap wow gold,cheapest wow gold store … buy euro gold wow wow gold–buy cheap wow gold,sell wow gold.welcome to buy cheap wow gold–cheap, easy, wow gold purchasing.World of Warcraft,wow gold Super …
    We can have your wow gold,buy wow gold,wow gold game,world of warcraft gold, wow Gold Cheap wow, Cheap wow gold,world of warcraft gold deal,Cheap WOW Gold …

    Welcome to our website for you World of Warcraft Gold,Wow Gold,Cheap World of Warcraft Gold,wow gold,buy cheap wow gold,real wow gold,sell wow gold, …
    Here wow gold of 1000 gold at $68.99-$80.99,World Of Warcraft Gold,buy wow gold,sell world of warcraft gold(wow gold),buy gold wow lightninghoof instock Cheap wow gold,cheapest wow gold store …
    wow gold–buy cheap wow gold,sell wow gold.welcome to buy cheap wow gold–cheap ffxi gil, easy, wow gold purchasing.World of Warcraft,wow gold Super …
    Wow gold- Gold for buy gold wow lightninghoof instock EU-Server: …wow Gold EU: starting from 84,99?; 3000 WoW Gold EU: starting from 119,99?.cheap ffxi gil wow Gold- Leveling Services: …
    We can have your wow Gold,buy wow Gold,wow Gold game,wow gold, Cheap wow Gold, Cheap World of Warcraft Gold,world of warcraft gold deal,buy cheap wow gold,Cheap WOW Gold …

    Here wow Gold of 1000 gold at $68.99-$80.99,World Of Warcraft Gold,buy wow Gold,sell world of warcraft gold(wow gold),Cheap wow gold,cheapest World of Warcraft Gold store …

5 trackbacks

Leave a Reply