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:

    1. @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.”
    1. 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.”

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.

You may also like...

19 Responses

  1. Pasquale Pio Francavilla says:

    Really really really really THANK YOU!

  2. Jit says:

    Thank you so much, it’s exactly what I needed

  3. Golfman says:

    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.

  4. Martin Borisov says:

    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.

  5. Swapnil Khole says:

    Made my day, thank you so much!

  6. Lavanya Sakala says:

    Thanks a lot for the post, it saved my time.

  7. Artemise says:

    Really really really really THANK YOU! 2

  8. Apoorv Jain says:

    Thanksss alotttttttt!!!!!!!!!!!!!!!

  9. Tony stark says:

    Thankyou 3000!!

  10. B says:

    +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

  11. Frédéric says:

    After 5 hours of investigation, of desperation, I came across this post, just magical !
    Thanks a lot !

  12. Adrian Perez says:

    the best explanation about @Transactional …thanks !

  13. Jair says:

    Thanks! This works for me =D

  14. Vaibhav bhanawat says:

    I moved method to different service class but still its not creating new transaction. Can someone help me

  15. Joneiry says:

    A mi no me funciona ya que tengo varios metodos dentro y no hace rollback si uno falla

  16. anonymous says:

    Thank you so much. It worked 🙂

  17. Jeevan says:

    Thank You out of my heart. Worked like a charm

  18. Shashi says:

    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.

  1. December 28, 2020

    […] 经过大量搜索,尝试和失败之后,我找到了此链接:http://ignaciosuay.com/why-is-spring-ignoring-transactional/ […]

Leave a Reply to Artemise Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.