Java Spring @Transactional method not rolling back as expected

后端 未结 3 1991
伪装坚强ぢ
伪装坚强ぢ 2020-12-29 00:09

Below is a quick outline of what I\'m trying to do. I want to push a record to two different tables in the database from one method call. If anything fails, I want everythin

相关标签:
3条回答
  • 2020-12-29 00:37

    I think the behavior you encounter is dependent on what ORM / persistence provider and database you're using. I tested your case using hibernate & mysql and all my transactions rolled back alright.

    If you do use hibernate enable SQL and transaction logging to see what it's doing:

    log4j.logger.org.hibernate.SQL=DEBUG
    log4j.logger.org.hibernate.transaction=DEBUG
    // for hibernate 4.2.2 
    // log4j.logger.org.hibernate.engine.transaction=DEBUG
    

    If you're on plain jdbc (using spring JdbcTemplate), you can also debug SQL & transaction on Spring level

    log4j.logger.org.springframework.jdbc.core=DEBUG
    log4j.logger.org.springframework.transaction=DEBUG
    

    Double check your autocommit settings and database specific peciular (eg: most DDL will be comitted right away, you won't be able to roll it back although spring/hibernate did so)

    0 讨论(0)
  • 2020-12-29 00:39

    I found the solution!

    Apparently Spring can't intercept internal method calls to transactional methods. So I took out the method calling the transactional method, and put it into a separate class, and the rollback works just fine. Below is a rough example of the fix.

    public class Foo {
        public void insertRecords(List<Record> records){
            Service myService = new Service();
            for (Record record : records){
                myService.insertIntoAAndB(record);
            }
        }
    }
    
    public class Service {
        MyDAO dao;
    
        @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
        public void insertIntoAAndB(Record record){
            insertIntoA(record);
            insertIntoB(record);
        }
    
        @Transactional(propagation = Propagation.REQUIRED)
        public void insertIntoA(Record record){
            dao.insertIntoA(record);
        }
    
        @Transactional(propagation = Propagation.REQUIRED)
        public void insertIntoB(Record record){
            dao.insertIntoB(record);
        }
    
        public void setMyDAO(final MyDAO dao) {
            this.dao = dao;
        }
    }
    
    0 讨论(0)
  • 2020-12-29 00:44

    Just because jdk parses aop annotation not only with the method, also parse annotation with the target class. For example, you have method A with @transactional, and method B which calls method A but without @transactional, When you invoke the method B with reflection, Spring AOP will check the B method with the target class has any annotations. So if your calling method in this class is not with the @transactional, it will not parse any other method in this method. At last, show you the source code: org.springframework.aop.framework.jdkDynamicAopProxy.class

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ......
        // Get the interception chain for this method.
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        if (chain.isEmpty()) {
        // We can skip creating a MethodInvocation: just invoke the target directly
        // Note that the final invoker must be an InvokerInterceptor so we know it does
        // nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
        // We need to create a method invocation...
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // Proceed to the joinpoint through the interceptor chain.
        retVal = invocation.proceed();
        }
    }
    
    0 讨论(0)
提交回复
热议问题