Wednesday, January 14, 2009

A note about Springs @Transactional annotation

The other day, on my current project, I had a situation where I needed to change some transaction parameters on a DAO to start a new transaction whenever methods on that DAO were invoked.

The application architecture is based on a generic DAO interface with standard CRUD operations implemented by a GenericDao. All DAOs have defined their own interface extending the generic DAO and likewise the implementation is extending the GenericDao:

The goal was to have all methods, defined on either the GenericDao or the (in this example) UserDaoImpl , to start a new transaction when invoked.

The GenericDao is annotated with Springs @Transactional annotation with default parameters (Propagation.REQUIRED) which will be inherited to all extending DAOs.

My first attempt to achieve my goal was to annotate the UserDaoImpl with @Transactional(propagation=Propagation.REQUIRED_NEW). Well, this did not have the effect I had hoped for. Now all methods declared on the UserDaoImpl will start a new transaction, but methods on the GenericDao will not and if I removed the annotation on the GenericDao and defined it on all DAOs then invoked methods on the GenericDao will not be transactional at all.

This forced me to investigate how Spring handles the @Transactional annotation and it all comes down to how the method getTransactionAttribute(Method method, Class targetClass) on the class AbstractFallbackTransactionAttributeSource works.

I'll try to explain:
- the method will be invoked where parameter method is Dao#update and targetClass is UserDaoImpl.

  1. The method is from the Dao interface so firstly the actual implementation will be found = GenericDao#update.
  2. This will be the first place to look for the annotation - on the method on the declaring class. Not the case here.
  3. If not on the method then look if the declaring class has defined the annotation. Yep - in this case the GenericDao has defined the annotation for default behavior for all extending DAOs, which means that a new transaction will not be started for invoked methods defined on GenericDao. If I override the method in UserDaoImpl and simply call super I would get the expected behavior, but I do not find this as a convenient solution.
  4. If the target class is a interface and the annotation is not yet present then the interface method and class is searched.
This seems like a good strategy to search for the annotation, but maybe you could argue that if the annotation is present directly on the targetClass (UserDaoImpl) then it should be used in advance of the annotation defined on the actual declaring class of the method in action. You could see this as a way to overwrite the annotaton on the super class.

In the above list this could be number 2.5 just after searching on the method then look at the targetClass before the declaring class... (if they differ at all...) What do you think? Is this a reasonable suggestion?

Well, back to my problem. My solution was of course keep the @Transactional on the GenericDao and remove it from UserDaoImpl and then create a new service in the service layer annotated with @Transactional(propagation=Propagation.REQUIRES_NEW) and actually I am more happy with this solution as it keeps the Dao operations simple and atomic.

3 comments:

Ollie said...

Shouldn't you not better transactions on servicelayer anyway? To me marking DAOs did never really span the right granularity.

Nevertheless, nice one to know about!

Regards,
Ollie

Anonymous said...

Overwriting on subclass level is not really very good. Caller is programming against an interface and may depend on certain promise made by it. Changing it in one of the implementations might break the code in subtle or not so subtle ways.

What possibly could work is some kind of covariance logic for the annotations - similar to design by contract postconditions. Interface promises A, subclass has to provide A and may additionally promise B, as long as B is stronger than A. Trick here would be to understand what does it mean to strengthen 'promise' in case of transactions

Dino said...

I actually work in a similar issue in my current project ...

even though I don't see starting a brand new transaction within every DAO method an efficient way to work with the database ... as the original idea of transactions is to pack collection of CRUD operations together in one transaction ...

I used a similar solution .. and started a new transaction with every method in DAO classes .. as it removes the headache of starting and ending transactions with every interaction with the DATABASE ... and then I made my own transaction managment classes... that suites my project...

but, I didn't use @Transactional ... as I'm a lazy coder .. I use a DAO generator which I don't like to modify its generated code coz I regenerate it very often ....

so I used AOP .. in spring ... which enabled me ...to :-

modify the database @ any time .. ,regenerate DAOs ... and transactions will still be supported from spring ....