hawarden_
hawarden_

Reputation: 2170

UnexpectedRollBackException in Spring inner transaction

I have two classes:

@Service
@Transaction
class A {
    public void method1() {
        private B;

        try {
            save1()
            b.method2()
        } catch (SqlException e) {
            doSomeThing();
        }

       @Autowired
       public setB(){
         this.B = B;
       }
    }
}

@Service
class B {

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void method2(){
        save2()
        throw new SqlException();
    }

}

I got an SqlException as expected, but also an UnexpectedRollBackException, and the program stops. I want to know why the data persisted by save2() is not rolled back?

Is it a problem with outer transaction?

UPDATE: I tried catching UnexpectedRollBackException in class A and everything works fine. But I still need some kind of explanation why I get the exception? I suppose the outer transaction should be suspended when the inner transaction begins, so why the rollback is unexpected for the outer transaction?

Thanks.

Upvotes: 7

Views: 5232

Answers (2)

Vipin CP
Vipin CP

Reputation: 3807

I have a simliar scenario like this and resolved it using propagation = Propagation.NESTED from where the second method is calling. rewrite the code as mentioned below and try.

@Service
class A {
@Transactional(rollbackFor = { HibernateException.class}, propagation = Propagation.NESTED)
    public void method1() {
        private B;

        try {
            save1()
            b.method2()
        } catch (SqlException e) {
            doSomeThing();
        }

       @Autowired
       public setB(){
         this.B = B;
       }
    }
}

Note: Transactional is used in method level for class A with propagation nested . Class B shown below.

@Service
class B {

   @Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRED)
    public void method2(){
        save2()
        throw new SqlException();
    }

}

This has resolved the issue in my case.

Upvotes: 0

Ilya Dyoshin
Ilya Dyoshin

Reputation: 4624

First of all: you are not injecting the instance of B into class A via spring. i.e. your b is not managed by spring => this leads to the behaviour: Spring is ignoring the @Transactional annotation on method.

Second if you don't want to rollback on SqlException you have to specify noRollbackFor=SqlException.class

UPDATE: after clarification, the call is happening on managed bean. But the problem that expected behaviour - transaction inside of transaction is not supported in general by the transaction management system out of the box. https://docs.spring.io/spring/docs/4.3.11.RELEASE/javadoc-api/org/springframework/transaction/annotation/Propagation.html#REQUIRES_NEW

Unless the details on that system are provided it is impossible to step forward. Out of content the NESTED transaction propagation could be better, then REQUIRES_NEW, because it is making a rollback point for the external transaction and starts new one.

Upvotes: 4

Related Questions