Spring’s @Transactional does not rollback on checked exceptions

18. February 2009

We’re using the Spring Framework in most of our applications (and thus also in the Catalysts Platform) and are really satisfied with it.

One of the big advantages is the the declarative transaction handling using the @Transactional attribute.

import org.springframework.transaction.Transactional;
 
@Transactional
public class MyService implements IMyService {
  public List getResults () {
    // do something
  }
 
  public void foo() {
    throw new java.lang.UnsupportedOperationException();
  }
 
  public void bar() {
    throw new java.lang.Exception();
  }
}

That simple annoation on class managed by a ApplicationContext causes all method calls onto that service to be bound to a transaction. The transaction is committed after the method call has left the service again and it’s rollbacked for the case an exception is thrown (e.g. after calling the (quite silly) method foo()).

But be careful: Only unchecked exceptions (that is, subclasses of .lang.RuntimeException) are rollbacked by default. For the case, a checked exception is thrown, the transaction will be committed!

The Spring documentation explains that as follows:

While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.

And that customization can be done very easily by just adding the parameter rollBackFor to the @Transactional attribute:

import org.springframework.transaction.Transactional;
 
@Transactional(rollbackFor = Exception.class)
public class MyService implements IMyService {
  public List getResults () {
    // do something
  }
 
  public void foo() {
    throw new java.lang.UnsupportedOperationException();
  }
 
  public void bar() {
    throw new java.lang.Exception();
  }
}

In that case, the transaction will even be be rollbacked on a call to the method bar().

Tags: ,

Click to send this page to Twitter! Click to share this page on Facebook! 

  1. Maksym Demidas
    3. June 2009 at 10:58 | #1

    Nice article! Very helpful)

  2. shekhar
    28. October 2009 at 10:03 | #2

    good artical

  3. shekhar
    30. October 2009 at 08:48 | #3

    Good Example

  4. Jes GP
    9. February 2010 at 09:33 | #4

    Great!

    Good Article. Thanks for it.

  5. 10. February 2010 at 04:26 | #5

    Why would you ever want throw a checked exception from the service layer up? What do you think the caller will do with them!?

    Normally you would deal with these bad checked exceptions within the service, so the caller does not have to clean the mess + if you really want to roll back on this checked exception, rethrow it as a Runtime (unchecked)

    /Toly

  6. 12. February 2010 at 08:17 | #6

    Well the problem of unchecked exceptions is that the caller never knows what can happen on a certain call and you can’t force him to react on it. Suppose you have a form with a couple of input fields and want to validate all the data. Of course you do that inside the service layer, or maybe even in the dao layer, and then e.g. use springs binding framework to throw a BindException (which is a checked exception). Now of course I could convert that later into a RuntimeException and throw that out to the view layer but even in that case the caller has to react on it, and e.g. show some error message. That’s much clearer if it’s a checked excpetion that forces the client to create a try/catch block. @Toly

  7. TejR
    6. January 2011 at 13:56 | #7

    “@Transactional(rollbackFor = Exception.class)” – Does this mean if all the service methods throw a subclass of any checked exception, they will all be rolled back?

    • Klaus Lehner
      6. January 2011 at 14:00 | #8

      Yes, if any of the methods that are called in there throw any subclass of Exception (or Exception itself), then the whole transaction will be rollbacked.

  8. TejR
    6. January 2011 at 13:57 | #9

    Sorry unchecked exception

    • Klaus Lehner
      6. January 2011 at 14:23 | #10

      unchecked exceptions extend RuntimeException which extends Exception so unchecked exception will be rolled back as well in that scenario

  9. Chris Micallef
    14. January 2011 at 16:47 | #11

    My problem is the other way round. I have rollbackFor=MyException.class …. however, my transaction is being rolled back even for Exceptions that are not of type MyException or one of its sub classes. Any help on this?

  10. 24. January 2011 at 08:51 | #12

    Hi,

    Nice article. Just a thought. @ApplicationException(rollback=true) on a user defined exception would do the same isnt it? Or any runtime exception will automatically roll back a transaction.

  11. vittal
    20. June 2011 at 14:32 | #13

    Nice Article , very userful

  12. 10. November 2011 at 00:44 | #14

    Great! Thank you for the article!

  13. chinna
    30. November 2011 at 17:35 | #15

    Hi

    Nice article, but would this work for below requirement.

    Once a transaction is started I saved many records in to many tables and if any exception ocurrs it should rollback all the records from database.

  14. Klaus Lehner
    7. December 2011 at 09:45 | #16

    @chinna, yes of course that would work, that’s the intention of all that stuff

  15. tripty
    18. December 2011 at 14:35 | #17

    hi,
    i am throwing checked exception . @transactional(rollbackFor=Exception.class) also added but still my transaction is getting commited.
    pls help .

  16. bluebone
    24. December 2011 at 00:21 | #18

    @tripty What propagation do you have? What is not being rolled back? Are you also calling the method directly? If you could show the class and the calls, it could clarify things. If you are calling just the method directly (ie. it is not a nested txn) then you should have a full rollback.