Reputation: 592
It seems that when a transactional spring method with propagation of NESTED calls another transactional method with propagation REQUIRED, the inner transaction can force the rollback of the outer logical transaction. Anyone can confirm?
I would like to handle RuntimeException and not rollback the outer transaction, example:
@Transactional
class A {
@Autowired
B b;
@Autowired
C c;
void methodA() { // outer transaction, this should not be rollback but currently getting UnexpectedRollbackException
b.methodB(() -> c.methodC());
}
}
@Transactional(propagation = Propagation.NESTED)
class B {
void methodB(Runnable action) { // inner nested transaction
try{
action.run();
} catch (Exception e){
// nothing
}
}
}
@Transactional
class C {
void methodC() { // inner required transaction
throw new RuntimeException();
}
}
Upvotes: 0
Views: 300
Reputation: 3654
Why wouldn't it? Propagation NESTED
begins a transaction within the current one if one exists, and behaves like REQUIRED
otherwise. The javadocs state:
/**
* Support a current transaction; create a new one if none exists.
* Analogous to the EJB transaction attribute of the same name.
* <p>This is typically the default setting of a transaction definition,
* and typically defines a transaction synchronization scope.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@link #PROPAGATION_REQUIRED} else. There is no analogous
* feature in EJB.
* <p><b>NOTE:</b> Actual creation of a nested transaction will only work on
* specific transaction managers. Out of the box, this only applies to the JDBC
* {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* when working on a JDBC 3.0 driver. Some JTA providers might support
* nested transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
int PROPAGATION_NESTED = 6;
Of note is that NESTED
is truly only supported if your JDBC driver supports savepoints. This means:
No existing transaction A (NESTED) B (REQUIRED)
Would have the following behaviour:
begin; -- called prior to A but in A's
A.doSomething();
B.doSomethingThatCausesException()
rollback;
And
Existing transaction A (NESTED) B (REQUIRED)
Would have the following behaviour:
begin; -- called outside of the scope of A
savepoint A_savepoint
A.doSomething();
B.doSomethingThatCausesException();
rollback A_savepoint;
if your JDBC driver supports nested transactions. Otherwise, it would behave like the first scenario. See also this answer.
That said, I believe that savepoints are more trouble than they're worth and you will save yourself a lot of potential trouble if you're treating any of your database operations atomically.
Upvotes: 0