I have a small Spring Boot application with spring-boot-starter-web
, spring-boot-starter-data-jpa
, and postgresql
as dependencies.
I'm able to use the @Transactional
annotation and use JPA to fetch and save entities to the database. However, if I were to add afterCommit
/afterCompletion
hooks via registering a synchronization, it gives an IllegalStateException
saying that Transaction synchronization is not active
.
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
//this doesn't get called
log.info("do something here");
}
});
Doing TransactionSynchronizationManager.initSynchronization();
gets rid of the error, but the hooks don't get called (eg: the afterCommit
hook doesn't get called even though the transaction has committed.)
Any clues on how to debug this?
Spring provides support for synchronizing resources with transactions since the earliest versions. We often use it to synchronize transactions managed by multiple transaction managers. For example, we can synchronize a JMS commit with a JDBC commit.
The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic. In the declarative approach, we annotate the methods with the @Transactional annotation.
The @Transactional annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, "start a brand new read-only transaction when this method is invoked, suspending any existing transaction".
Spring Boot implicitly creates a proxy for the transaction annotated methods. So for such methods the proxy acts like a wrapper which takes care of creating a transaction at the beginning of the method call and committing the transaction after the method is executed.
It turned out that I had forgotten to include the build plugin that is used to create the AoP-proxies for beans having @Transactional annotations.
In the absence of this plugin, no proxies would get generated, and the code would run non-transactionally; except for when it enters the JpaRepository
methods where it would create a short-lived transaction for the duration of the call (such as save
/findAll
/delete
).
This is the plugin that I missed including in my pom.xml (this got generated in the pom output by the spring initializr (https://start.spring.io/) but I didn't notice it at first and didn't copy it over into my pom)
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With