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

前端 未结 8 2170
谎友^
谎友^ 2020-11-22 05:07

I am new to Spring Transaction. Something that I found really odd, probably I did understand this properly.

I wanted to have a transactional around method level and

相关标签:
8条回答
  • 2020-11-22 05:31

    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);
            }
        } 
    }
    
    0 讨论(0)
  • 2020-11-22 05:31

    This is my solution for self invocation:

    public class SBMWSBL {
        private SBMWSBL self;
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @PostConstruct
        public void postContruct(){
            self = applicationContext.getBean(SBMWSBL.class);
        }
    
        // ...
    }
    
    0 讨论(0)
  • 2020-11-22 05:38

    Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advised, as it may look strange to colleagues. But it works with singletons, is easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution as described in Espens answer.

    @Service
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    class PersonDao {
    
        private final PersonDao _personDao;
    
        @Autowired
        public PersonDao(PersonDao personDao) {
            _personDao = personDao;
        }
    
        @Transactional
        public void addUser(String username, String password) {
            // call database layer
        }
    
        public void addUsers(List<User> users) {
            for (User user : users) {
                _personDao.addUser(user.getUserName, user.getPassword);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 05:45

    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>
    
    0 讨论(0)
  • 2020-11-22 05:45

    You can autowired BeanFactory inside the same class and do a

    getBean(YourClazz.class)

    It will automatically proxify your class and take into account your @Transactional or other aop annotation.

    0 讨论(0)
  • 2020-11-22 05:46

    The issue is related to how spring load classes and proxies. It will not work , untill you write your inner method / transaction in another class or go to other class and then again come to your class and then write the inner nested transcation method.

    To summarize, spring proxies does not allow the scenarios which you are facing. you have to write the 2nd transaction method in other class

    0 讨论(0)
提交回复
热议问题