Spent 30 minutes wondering why my Spring Boot @Transactional annotation was not rolling back on exceptions. The code looked fine: @Transactional public void processOrder(Order order) { orderRepository.save(order); paymentService.charge(order); // throws exception } An exception was thrown. But the order was still saved in the database. The problem: @Transactional only rolls back on unchecked exceptions (RuntimeException) by default. The payment service was throwing a checked exception. The fix: @Transactional(rollbackFor = Exception.class) public void processOrder(Order order) { orderRepository.save(order); paymentService.charge(order); } One attribute. That was it. Spring does not warn you when checked exceptions bypass rollback. It just commits the transaction anyway. What @Transactional behavior has surprised you? #Java #SpringBoot #Debugging #BackendDevelopment
I’ve just shared some @Transactional quirks yesterday. This is definitely one of them https://www.garudax.id/posts/imretomosvari_youve-usedtransactionalhundreds-of-times-activity-7434957093826236416-_bqn
Why not having everything throws possibly a runtimeException ? I mean it's a habit of mine to catch every exception to rethrow a runtimeException (adding context, error code and correct error message). The main reason is the ability to use the code with functional programming and streams… Genuine question… 😉
Another common surprise with @Transactional is self-invocation. When a method inside the same class calls another transactional method, the Spring proxy is bypassed, so the transaction isn’t applied. Splitting the logic into separate services usually avoids this issue.
Great catch !!
This one caught me off guard because I assumed all exceptions would trigger rollback. Adding rollbackFor = Exception.class is now part of my default setup for critical transactions.