In Kotlin all exceptions are effectively unchecked. Therefore, transactions will not be rolled back for functions like this:
@Transactional
fun example(): Unit {
runQuery()
throw MyCheckedException()
runQuery()
}
to achieve a correct behavior, the above code needs to be refactored as follows:
@Transactional(rollbackFor = [Exception::class])
fun example(): Unit {
runQuery()
throw MyCheckedException()
runQuery()
}
this isn't very intuitive and can lead to unexpected results. Furthermore, even if a developer is aware of this, he/she should not forget to specify rollbackFor for every @Transactional annotation.
It should be possible to configure application-wide defaults for rollbackFor and noRollbackFor
@ilyastam as stated here: https://docs.spring.io/spring/docs/5.2.0.RC1/spring-framework-reference/data-access.html#transaction-declarative-rolling-back
Transactions are rolled back by unchecked exceptions by default.
So it should work out of the box with Kotlin because exceptions are unchecked https://kotlinlang.org/docs/reference/exceptions.html
Do you have a sample projects that reproduces this issue?
Kotlin doesn't distinguish between RuntimeException (unchecked) and Exception (checked), meaning Kotlin code can freely throw either type. Spring transactional support does distinguish between the two by not rolling back any checked exception by default (an exception subclassing Exception).
One can choose to only use RuntimeException derived exceptions in Kotlin to avoid that issue, but it can also arise when Kotlin code calls into a Java method that throws a checked Exception. Essentially it's a "foot gun", one mistake can lead to the unexpected behavior of a transaction not being rolled back.
The issue is that in a transactional method we can call a method which can throw IOException, and in this case the transaction will not rollback already mutated data because IOException is not a subclass of RuntimeException
Why not to rollback by default by Exception and not by RuntimeException without any configs?
This will not break the Java code.
How about specifying rollback behavior in a @Transactional annotation at the class level?
@elab that would mark all public methods in the class to be transactional.
Most helpful comment
The issue is that in a transactional method we can call a method which can throw
IOException, and in this case the transaction will not rollback already mutated data becauseIOExceptionis not a subclass ofRuntimeExceptionWhy not to rollback by default by
Exceptionand not byRuntimeExceptionwithout any configs?This will not break the Java code.