Why is Spring ignoring @Transactional?
My problem:
I was working in a service and I wanted to run one of the methods in a new transaction in order to commit the changes to the database after exiting the method.
My code was similar to:
@Service public class MyService { @Autowired MyEntityRepository myEntityRepository; public void mainMethod(){ MyEntity myEntity = processData(); MyEntity savedEntity = persistData(myEntity); notify3rdParty(savedEntity); } @Transactional(propagation = Propagation.REQUIRES_NEW) private MyEntity persist(){ myEntityRepository.save(myEntity); } }
As you can see I was adding the “@Transactional(propagation = Propagation.REQUIRES_NEW)” annotation above the persist method because I wanted to run that method in a different transaction and commit the data to the database before leaving the method.
The problem that I was having is that the @Transactional annotation was being ignored and the notify3rdParty method was being invoked before the data was committed to the database.
First thing I did was to set the springframerwork.transaction logs level to DEBUG and that help me to realize that the @Transactional annotation was being ignored:
log4j.logger.org.springframework.orm.jpa=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
Why the @Transactional annotation is being ignored?
The annotation is being ignored mainly for 2 reasons:
-
- @Transactional will only work over public methods, so this annotation will be always ignored if it is above a private, protected or package-protected method.
According to the Spring documentation:
“When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.”
- @Transactional will only work over public methods, so this annotation will be always ignored if it is above a private, protected or package-protected method.
-
- The method is in the same class where it is invoked. You need to have the method in a different class where the method is invoked.
According to the Spring documentation:
“In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. @PostConstruct.”
- The method is in the same class where it is invoked. You need to have the method in a different class where the method is invoked.
Solution:
In my case I solved the problem by creating a new service (“MyEntityService”). Then I moved the persist method to the new class and I changed the method access modifier to public:
@Service Public class MyEntityService { @Autowired MyEntityRepository myEntityRepository; @Transactional(propagation = Propagation.REQUIRES_NEW) public MyEntity persist(){ myEntityRepository.save(myEntity); } } @Service public class MyService { @Autowired MyEntityService myEntityService; public void mainMethod(){ MyEntity myEntity = processData(); MyEntity savedEntity = myEntityService.persistData(myEntity); notify3rdParty(savedEntity); } }
Special thanks to my colleague Martin Borisov for pointing me in the right direction.
Really really really really THANK YOU!
Thank you so much, it’s exactly what I needed
This is really amazing. The fact that so many people seek out and learn workarounds for this obviously ‘awkward’ ORM (to say the least) just baffles me.
Ideally people should seek out and learn about other ORMs that are out their that don’t suffer from these obvious design/implementation deficiencies.
You could really discover an awesome place to be as a developer is writing apps using a well designed ORM (i.e. not Hibernate) where your models just persist as you would expect without inventing arbitrary services or enabling the CPU cycle drain of AOP.
OMG this should be just another nail in the Hibernate coffin but sheeple just keep using it… developers, your life can be so much better.
This is how Spring AOP works and has nothing to do with Hibernate. You can also use TransactionTemplate, if you find it a better suite.
Made my day, thank you so much!
Thanks a lot for the post, it saved my time.
Really really really really THANK YOU! 2
Thanksss alotttttttt!!!!!!!!!!!!!!!
Thankyou 3000!!
+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
After 5 hours of investigation, of desperation, I came across this post, just magical !
Thanks a lot !
the best explanation about @Transactional …thanks !
Thanks! This works for me =D
I moved method to different service class but still its not creating new transaction. Can someone help me
A mi no me funciona ya que tengo varios metodos dentro y no hace rollback si uno falla
Thank you so much. It worked 🙂
Thank You out of my heart. Worked like a charm
main issue if method call chaining inside the service then proxy does not work like m1 call to m2 and apply transaction handling on m2 the it does not work. Solution controller directly calls to service method where transitional apply where it create the proxy and transnational rollback works fine.