spring-projects / spring-data-jpa

Simplifies the development of creating a JPA-based data access layer.
https://spring.io/projects/spring-data-jpa/
Apache License 2.0
3.03k stars 1.42k forks source link

Not expecting UnexpectedRollbackException #3585

Closed ctzen closed 3 months ago

ctzen commented 3 months ago

A synthetic (typical?) merge operation handling OptimisticLockException:

try {
    transactionTemplate.execute(_ -> entityManager.merge(staleFoo));  // throws OptimisticLockException
} 
catch (OptimisticLockException e) {
    var freshFoo = reloadAndUpdateFooProperties();
    transactionTemplate.execute(_ -> entityManager.merge(freshFoo));  // success
}

The above works fine if there is no active transaction at the time of entry.

transactionTemplate.execute(_ -> { 
    try {
        transactionTemplate.execute(_ -> entityManager.merge(staleFoo));  // throws OptimisticLockException
    } 
    catch (OptimisticLockException e) {
        var freshFoo = reloadAndUpdateFooProperties();
        transactionTemplate.execute(_ -> entityManager.merge(freshFoo));  // success
    }
});  // cause UnexpectedRollbackException

Causes an UnexpectedRollbackException Transaction silently rolled back because it has been marked as rollback-only.

Why would the same code behaves differently depending on if there is an existing active transacton? Is this a bug? or I am using transactions wrongly?

Attached sample project to demonstrate the behaviour. tx-issue.tar.gz

mp911de commented 3 months ago

You experience UnexpectedRollbackException because an exception has crossed a transactional boundary (the inner call to transactionTemplate.execute), and therefore, the transaction is marked as rollback-only.

In any case, Transaction Management is provided by Spring Framework and the issue belongs into Spring Framework if you have further questions.