Search code examples
springtransactional

Spring @Transactional placement


I'm recently playing a little with Spring/JPA2 to understand better how it works. During my experiments I found some strange behaviour. The question is:

Why the following code works well (confirmed record added in db):

@Repository
public class UserDAO {

    @PersistenceContext
    EntityManager em;

    @Transactional
    public void add(User user) {
        doAdd(user);
    }

    public void doAdd(User user) {
        em.persist(user);
    }
}

But the following (@Transactional annotation moved to inner method):

@Repository
public class UserDAO {

    @PersistenceContext
    EntityManager em;

    public void add(User user) {
        doAdd(user);
    }

    @Transactional
    public void doAdd(User user) {
        em.persist(user);
    }
}

Throws exception:

javax.persistence.TransactionRequiredException: No transactional EntityManager available
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:273)
    at com.sun.proxy.$Proxy556.persist(Unknown Source)
    at com.example.UserDAO.doAdd(UserDAO.java:24)
    ...

Solution

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

    Source

    The @Transactional annotation support works by wrapping the actual DAO instance in a Proxy, which intercepts the method calls and starts/commits the transaction. In the second example the actual UserDAO instance is calling the doSave method and therefore there is no Proxy to intercept the method call.