How to rollback to savepoint nested transactions using Hibernate
Asked Answered
S

3

12

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
    }
}
Seiter answered 25/11, 2013 at 16:58 Comment(0)
C
8

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

Citral answered 11/2, 2014 at 13:0 Comment(0)
N
0

There is no support for nested transactions in Spring/Hibernate/Java EE. So, either the whole thing is rollbacked, or the inner transaction is actually a new, different transaction, that will be committed as soon as it succeeds, and even if the outer transaction rollbacks later.

If the latter is what you want, then simply annotate your inner method with

@Transactional(propagation = Propagation.REQUIRES_NEW)
Notecase answered 25/11, 2013 at 17:27 Comment(1)
I added this to the inner method, and I get a deadlock! Any ideas?Seiter
L
0

Try setting the globalRollbackOnParticipationFailure attribute of your TransactionManager to false.

See http://docs.spring.io/spring/docs/3.1.4.RELEASE/javadoc-api/org/springframework/transaction/support/AbstractPlatformTransactionManager.html#setGlobalRollbackOnParticipationFailure(boolean) for more information.

Lousewort answered 11/2, 2014 at 9:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.