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.
Hence the UnexpectedRollbackException. To prevent this, the only thing you need to do is catch (and swallow, if you really want to ignore this problem) the original ConstraintViolationException WITHIN the transaction. Inside the saveMyData() for example would be ok.
The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.
Internally, its the same as using a transaction advice (using AOP), where a proxy is created first and is invoked before/after the target bean's method. The generated proxy object is supplied with a TransactionInterceptor, which is created by Spring.
The @Transactional annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, "start a brand new read-only transaction when this method is invoked, suspending any existing transaction".
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With