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

2 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

Leave a Reply

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