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

  2. 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.”

  3. 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...

5 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!

Leave a 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.