-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Description
Problem
It's common practice to use a simple (no custom attributes) @Transactional
on methods to execute them wrapped in a transaction. Unfortunately, for methods declared as @TransactionalEventListener
, this doesn't work as expected:
@Component
class SomeListener {
@Transactional
@TransactionalEventListener
void on(SomeEvent event) { /* … */ }
}
Those methods are invoked in the transaction cleanup phase of an original, other transaction. This means that this particular, other transaction is still running, although in an undefined state. With a simple @Transactional
declared on those methods, the code would be executed in an undefined transactional state. An obvious solution to this problem is to declare a Propagation
of REQUIRES_NEW
on the listener method, so that it will run in a new transaction:
@Component
class SomeListener {
@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener
void on(SomeEvent event) { /* … */ }
}
Proposed solution
It would be nice if the erroneous arrangement shown above would be rejected, ideally at bootstrap (potentially even AOT?) time. We have to consider that a plain @Transactional
is still valid in the case of the method declared to be invoked asynchronously (using @Async
). In other words, a declaration like this is still valid, as the asynchronous invocation triggers the execution on a separate thread and thus a clean slate regarding transactions.
@Component
class SomeListener {
@Async
@Transactional
@TransactionalEventListener
void on(SomeEvent event) { /* … */ }
}