We are migrating a Spring Boot application from version 2.7 to version 3.3, along with an Open Liberty server (latest Version) and an Oracle 19 database.
Our services have a
@Transactional(readOnly = true)
annotation at the class level.
After upgrading to Spring Boot 3.3, we encountered the following issue. When we call, for example, a simple find method from our service, we get the error:
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout)
We have found that this error occurs when we throw a business exception of type RuntimeException
, which is handled in the service layer. This exception causes the aforementioned error (UnexpectedRollbackException
) to occur, as a RuntimeException
leads to a rollback.
On the old system, this error does not occur. That's why we looked further into it. On the old system, Spring automatically uses the WebSphereUowTransactionManager
. On the new system, Spring uses the JtaTransactionManager
.
Our hypothesis is that the WebSphereUowTransactionManager
and JtaTransactionManager
behave differently. It appears that the WebSphereUowTransactionManager
does not cause an UnexpectedRollbackException
with a @Transactional(readOnly = true)
annotation.
We have tried to set up the WebSphereUowTransactionManager
under Spring Boot 3.3, but we have not been successful, as this has been removed from spring-tx 6.
Alternative:
If we change our @Transactional(readOnly = true)
annotation to @Transactional(readOnly = true, noRollbackFor = CustomApplicationException.class)
, everything seems to work fine so far.
Question 1:
How can we use the WebSphereUowTransactionManager
under Spring Boot 3.3?
Question 2: Is the alternative a viable solution to bypass the issue? Do you foresee any problems with this approach?
Question 1:
The WebSphereUowTransactionManager has been deprecated and removed in recent versions of Spring, which aligns with the overall move towards more standardized JTA transaction management. As you observed, Spring Boot 3.3 relies on the JtaTransactionManager by default.
Given the removal of WebSphereUowTransactionManager in Spring 6, you have limited options:
Custom Transaction Manager: You could implement your own custom transaction manager that mimics the behavior of WebSphereUowTransactionManager. This would involve subclassing JtaTransactionManager and customizing the behavior to suit your needs. However, this is a complex and error-prone solution.
Stick to Standard JTA: Adapt your application to work with the JtaTransactionManager and modify the transaction handling as needed.
Given these points, the best course of action might be to adapt your application to work correctly with the JtaTransactionManager.
Question 2:
Using @Transactional(readOnly = true, noRollbackFor = CustomApplicationException.class) to bypass the issue is indeed a practical and straightforward solution. Here are some considerations:
Pros:
Simplicity: It is a simple change that aligns with how transaction management should work in a JTA environment.
Control: It gives you explicit control over which exceptions should trigger a rollback, allowing more granular transaction management. Cons and Potential Issues: Potential Overlook of Rollbacks: If your business logic evolves and new exceptions need to be handled similarly, you must remember to update the noRollbackFor attribute accordingly.
Consistency: Ensure that all services and methods that handle the CustomApplicationException are consistently annotated to prevent unexpected rollbacks in different parts of your application. Business Logic Coupling: Business exceptions and transaction management are tightly coupled, which can sometimes lead to less clear separation of concerns. This might be acceptable but should be managed carefully.