Java Spring中的@Transactional方法未按预期回滚

8
以下是我想要做的简要概述。我希望从一个方法调用中将记录推送到数据库的两个不同表中。如果有任何失败情况,我希望所有操作都能够回滚。因此,如果 insertIntoB 失败,我希望在 insertIntoA 中提交的任何内容都能够回滚。
public class Service {
    MyDAO dao;

    public void insertRecords(List<Record> records){
        for (Record record : records){
            insertIntoAAndB(record);
        }
    }

    @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;
    }
}

MyDAO dao是一个接口,使用mybatis映射到数据库,并通过Spring注入进行设置。

现在,如果insertIntoB失败,从insertIntoA推送到数据库的所有内容仍然会被记录。我该如何更正这种行为?

编辑:

我修改了类以更准确地描述我想要实现的目标。如果直接运行insertIntoAAndB,如果有任何问题,回滚就会起作用,但如果我从insertRecords调用insertIntoAAndB,如果出现任何问题,回滚将不起作用。

3个回答

17

我找到了解决方案!

显然Spring不能拦截对事务方法的内部方法调用。因此,我将调用事务方法的方法取出并放入一个单独的类中,这样回滚就可以正常工作了。下面是修复的简单示例:

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;
    }
}

还可以参考这个链接:https://dev59.com/fm855IYBdhLWcg3wXC19;如果你使用AspectJ而不是Spring代理,Spring可以拦截来自同一类的方法调用(@Transactional也适用于私有方法)。 - krisy

2

虽然 jdk 只解析使用 aop 注释的方法,但也会解析目标类中的注释。 例如,您有带有 @transactional 的方法 A,以及调用方法 A 但没有 @transactional 的方法 B。当您使用反射调用方法 B 时,Spring AOP 会检查目标类中的 B 方法是否有任何注释。 因此,如果您在该类中调用的方法没有使用 @transactional,则不会解析该方法中的任何其他方法。 最后,向您展示源代码: 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();
    }
}

2

我认为你遇到的行为取决于你使用的ORM /持久性提供程序和数据库。我使用Hibernate和MySQL测试了你的情况,所有的事务都回滚了。

如果你使用Hibernate,请启用SQL和事务日志记录以查看它的操作:

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

如果你正在使用普通的jdbc(使用Spring JdbcTemplate),你也可以在Spring级别上调试SQL和事务。
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG

请仔细检查您的自动提交设置和数据库特定的规则(例如:大多数DDL将立即提交,您将无法回滚,尽管Spring / Hibernate这样做)。


请发布您的Spring配置。 - Nicholas
1
@gerrytan 我编辑了我的问题,以更准确地描述我正在做的事情。 - Franklin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接