Spring @Transaction method call by the method within the same class, does not work? Spring @Transaction method call by the method within the same class, does not work? java java

Spring @Transaction method call by the method within the same class, does not work?


It's a limitation of Spring AOP (dynamic objects and cglib).

If you configure Spring to use AspectJ to handle the transactions, your code will work.

The simple and probably best alternative is to refactor your code. For example one class that handles users and one that process each user. Then default transaction handling with Spring AOP will work.


Configuration tips for handling transactions with AspectJ

To enable Spring to use AspectJ for transactions, you must set the mode to AspectJ:

<tx:annotation-driven mode="aspectj"/>

If you're using Spring with an older version than 3.0, you must also add this to your Spring configuration:

<bean class="org.springframework.transaction.aspectj        .AnnotationTransactionAspect" factory-method="aspectOf">    <property name="transactionManager" ref="transactionManager" /></bean>


The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). One solutions is already mentioned. Another nifty one would be to simply have Spring inject an instance of the service into the service itself, and call your method on the injected instance, which will be the proxy that handles your transactions. But be aware, that this may have bad side effects too, if your service bean is not a singleton:

<bean id="userService" class="your.package.UserService">  <property name="self" ref="userService" />    ...</bean>public class UserService {    private UserService self;    public void setSelf(UserService self) {        this.self = self;    }    @Transactional    public boolean addUser(String userName, String password) {        try {        // call DAO layer and adds to database.        } catch (Throwable e) {            TransactionAspectSupport.currentTransactionStatus()                .setRollbackOnly();        }    }    public boolean addUsers(List<User> users) {        for (User user : users) {            self.addUser(user.getUserName, user.getPassword);        }    } }


In Java 8+ there's another possibility, which I prefer for the reasons given below:

@Servicepublic class UserService {    @Autowired    private TransactionHandler transactionHandler;    public boolean addUsers(List<User> users) {        for (User user : users) {            transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));        }    }    private boolean addUser(String username, String password) {        // TODO call userRepository    }}@Servicepublic class TransactionHandler {    @Transactional(propagation = Propagation.REQUIRED)    public <T> T runInTransaction(Supplier<T> supplier) {        return supplier.get();    }    @Transactional(propagation = Propagation.REQUIRES_NEW)    public <T> T runInNewTransaction(Supplier<T> supplier) {        return supplier.get();    }}

This approach has the following advantages:

  1. It may be applied to private methods. So you don't have to break encapsulation by making a method public just to satisfy Spring limitations.

  2. Same method may be called within different transaction propagations and it is up to the caller to choose the suitable one. Compare these 2 lines:

    transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));

    transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));

  3. It is explicit, thus more readable.