How to rollback to savepoint nested transactions using Hibernate

hfm picture hfm · Nov 25, 2013 · Viewed 12.6k times · Source

I have a JavaEE application using Hibernate to connect to the database. In some part of my application I have calls to method which have a @Transactional annotation. In some of these cases, I want to rollback the whole transaction (the outer-service-method call, and the inner). And on some occasions I want to rollback only the inner service-method call (that is, rollback to savepoint defined at the start of the internal method).

The first part is already in place, but I have a problem with the second one. When I do the following, I get a "UnexpectedRollbackException" with the message "Transaction rolled back because it has been marked as rollback-only".

@Service
public class OuterService{

    @AutoWired
    private InnerServcie innerService; 

    @Transactional
    public void outer(){
        try{
            innerService.inner();
        }catch(RuntimeException e){
            //if i dont throw this up, it will give me the "UnexpectedRollbackException"
            System.out.println("I cought a RuntimeException");
        }
    }
}

@Service
public class InnerServcie{
    @Transactional
    public void inner(){
        //here we insert some data into db using hibernate
        //but something goes wrong and an exception is thrown
    }
}

Answer

Danubian Sailor picture Danubian Sailor · Feb 11, 2014

The feature you are looking for is called savepoints. They are not, strictly saying, nested transactions, but milestones in the consequent SQL instruction chain, to which you can rollback. Rolling back to savepoint means invalidating ALL instructions issued from the moment of creating the savepoint, so you can have multiple savepoints, but you can only rollback instructions between now and savepoint, not between 2 savepoints!

Spring supports savepoints, both when using JdbcTransactionObjectSupport manually, and using @Transactional annotation.

According to the document http://docs.spring.io/spring/docs/2.5.3/reference/transaction.html point 9.5.7.3 you should use Propagation.NESTED.

However, that options may not be available in your case. From Javadoc:

Note: Actual creation of a nested transaction will only work on specific transaction managers. Out of the box, this only applies to the JDBC DataSourceTransactionManager when working on a JDBC 3.0 driver. Some JTA providers might support nested transactions as well.

As last resort, you can issue SQL instructions starting/rollbacking to savepoint directly.

For PostgreSQL it would be:

SAVEPOINT foo;

ROLLBACK TO SAVEPOINT foo;

Source: http://www.postgresql.org/docs/8.2/static/sql-rollback-to.html