Affects:
spring
v6.0.12
spring-boot
v3.1.2
language
java (v17)
When an event listener annotated with @TransactionalEventListener
calls a method with the @Transaction(propagation = REQUIRED)
annotation, it participates in the original transaction.
When the Event Publisher commits, it clears the synchronizations
by invoking the TransactionSynchronizationManager.clearSynchronization()
method.
As a result, when the EventListener tries to get transaction, it determines newSynchronization
in TransactionStatus
to be true
,and then prepareSynchronization()
method is invoked.
prepareSynchronization()
method set transactionName by calling TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
Due to this behavior, the same transaction using the same transactional resource will have a different transaction name, which can be confusing.
Below is the example code.
Event Publisher
[Transaction name] practice.event.BookReturnService.returnBook
@Transactional
public void returnBook() {
log.info("tx: " + TransactionSynchronizationManager.getCurrentTransactionName());
var event = new BookReturnedEvent("clean code");
eventPublisher.publishEvent(event);
}
Event Listener
[Transaction name] practice.event.BookReturnService.returnBook
@TransactionalEventListener
public void listen(BookReturnedEvent event) {
log.info("tx: " + TransactionSynchronizationManager.getCurrentTransactionName());
client.doSomething();
}
Transactional method invoked by Listener
[Transaction name] practice.event.AbcClient.doSomething
@Transactional(propagation = Propagation.REQUIRED)
public void doSomething() {
log.info("tx: " + TransactionSynchronizationManager.getCurrentTransactionName());
List<Book> all = bookRepository.findAll();
Book book = bookRepository.save(new Book(Integer.toString(id)));
}
This is my first issue report, so I may be a bit clumsy. I apologize in advance!
Comment From: snicoll
When an event listener annotated with @TransactionalEventListener calls a method with the @Transaction(propagation = REQUIRED) annotation, it participates in the original transaction.
You're not supposed to do that. The default phase for the listener is AFTER_COMMIT
which states:
Handle the event after the commit has completed successfully. Note: This is a specialization of AFTER_COMPLETION and therefore executes in the same sequence of events as AFTER_COMPLETION (and not in TransactionSynchronization.afterCommit()). Interactions with the underlying transactional resource will not be committed in this phase
Whatever you're doing might just work by accident. It's hard to know for sure as the transaction configuration is not provided. Please provide a small application we can run ourselves that demonstrates the issue you've described. You can attach a zip to this issue or push the code to a separate GitHub repository.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.