views:

321

answers:

4

I am trying to use the declarative transaction management feature provide by Spring. I have set the spring configs and beans as described in reference documentation (i.e including AOP, tx namespaces and using the <tx:annotation-driven /> tag) and am using the @Transactional annotation on the method I want to be made transactional.

This is how the code looks like :

public interface Worker {
    public workOnEvents(List<Events> eventsForACustomer);
}

public class WorkerImpl {
    @Transactional
    public workOnEvents(List<Events> eventsForACustomer) {
        for(Event event : eventsForACustomer) {
            // get DAO's based on event types at runtime,
            // so we will have different DAO's acting within this loop
            DAOFactory.getDAO(event.getType()).persistEvent(event);
        }
    }
}

Now, I want that if any of the DAOs in the loop above fails to handle the event, all changes made to the database by other DAOs that came in the loop before this one, should get rolled back.

So to test the rollback, I took a list of some events say (e1, e2, e3) which would result in picking up of DAOs say (d1, d2, d3) and then I intentionally throw a runtime exception in the persistEvent method of DAO d2. However, the result I get is that the program terminates without moving on to event e3 in the loop, not handling the exception thrown. Also, the data persisted by DAO d1 is not rolled back.

Please let me know what could I be doing wrong here?

A: 

You need to add a RollbackFor attribute to the annotation. Something like this: @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Throwable.class)

Arvind
propagation=Propagation.REQUIRED is the default, so not really necessary.
Padmarag
Is that not already a default ? I think the documentation says that runtime exceptions are handled by default ?
abhinav
are you doing a flush() somewhere - I have seen transactions being committed if a flush is called...
Arvind
I have checked that, no flushes ... I will post some sample DAO code and my settings as well asap.
abhinav
A: 

Only unchecked exceptions (that is, subclasses of java.lang.RuntimeException) are rollbacked by default.

You need to add rollbackFor to @Transactional annotation.

You also do not have Exception handling, so e3 won't get executed.

Padmarag
As I mentioned I throw a RuntimeException so that should not be a problem. However, if I catch the exception how will that get propogated to Spring transcational layer so that it can rollback ?
abhinav
Could you also post the code of your DAO d2?
Padmarag
Please also add the code of DAO d1. There may be a commit/flush somewhere.
Padmarag
The DAO code just sets values into the entity to be persisted using some predefined configs, and finally executes this to persist the entity : getHibernateTemplate.saveOrUpdate(entity);
abhinav
A: 

Probably you already committed the previous transaction so the Java part can't roll it back anymore.

I guess either:

  • your DB commit every insert/update
  • your DAO did it
RichN
+1  A: 

Your expectations of what should happen are not going to get met with the annotation approach. The way it works is the annotation marks the method as something that Spring applies advice to, and the advice wraps the method, so stuff can happen before the method is called, like starting a new transaction, and after the method is finished, like committing or rolling back the transaction. When you describe that you want e1 and e2 rolled back, then e3 ought to be committed, that would require transaction code to get executed in the middle of the method, so using advice for that won't work.

For the level of control over transactions that you want, you're going to have to resort to Spring's programmatic transaction API.

EDIT: Looking at your comment I may have misinterpreted what you want. If you can identify a set of things you want to go in together, all or nothing, you can group them together in a method that you mark transactional, and have a non-transactional method that calls the method in a loop with exception-handling around each call of the transactional method.

Nathan Hughes
Thanks for the answer. Yes I achieved what I wanted by using the programmatic transaction API. My intent is this : For a customer a certain set of events are picked up (according to today's date and business rules) and they need to be handled (say, send mail). However, all the handling for events picked up for a customer should be handled in a transactional manner, so even if one fails, any changes made to db for that customer should get rolled back. Also, errors encountered while handling events for a customer should not cause termination of application.
abhinav
If I understand you right, you can manage with annotations if the transactional method is for everything specific to a given customer and your looping and exception-handling is outside of that. Hope you're not sending the email as part of the transaction, I've seen people try to do that and come to grief.
Nathan Hughes