spring-bootspring-data-jpa

Spring TransactionTemplate does not roll back JPA entity changes despite setRollbackOnly()


I’m using TransactionTemplate in a Spring Boot application to process user-specific order logic in a loop. I expect that if one user’s processing fails, their changes are rolled back, but the others are committed individually. However, I’m seeing that even after marking the transaction rollback-only (status.setRollbackOnly()), the OrderEntity status is still being persisted as COMPLETED instead of staying PENDING.

Here’s a simplified version of my setup:


users.forEach(user -> {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.executeWithoutResult(status -> {
        try {
            new UserOrderExecutor(user, context).execute();
        } catch (Exception e) {
            status.setRollbackOnly();
        }
    });
});

All the logic inside execute which saves modified orders and modifies user balance are not rolled back for the user.

I tried the following:

But still no luck. Any ideas what I am doing wrong?


Solution

  • There are at least some problems I see with your code.

    1. You are re-constructing the TransactionTemplate and it doesn't really add anything, assuming the method this code is in is transactional itself.
    2. You want individual transactions then you should set the proper isolation level (on the TransactionTemplate
    3. Due to 2 the fact that you catch the exception makes it not fail and everything will be committed as the over-arching TX will still commit.

    You should create the TransactionTemplate only once and you should specify the correct isolation level.

    var tx = new TransactionTemplate(transactionManager);
    tx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW)
    users.forEach(user -> {
      tx.executeWithoutResult(x -> new UserOrderExecutor(user, context).execute());
    });
    

    This will execute exit operation for a User in a dedicated transaction and only rollback that specific user.

    NOTE: If your list of users is large I would suggest to use Spring Batch to handle the updated users and error handling.