Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/363.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring@Transactional方法未按预期回滚_Java_Spring_Transactions - Fatal编程技术网

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

Java Spring@Transactional方法未按预期回滚,java,spring,transactions,Java,Spring,Transactions,下面是我试图做的一个简要概述。我想从一个方法调用将记录推送到数据库中的两个不同表中。如果失败了,我希望一切都能恢复。因此,如果insertIntoB失败,我希望将在insertIntoA中提交的任何内容回滚 public class Service { MyDAO dao; public void insertRecords(List<Record> records){ for (Record record : records){

下面是我试图做的一个简要概述。我想从一个方法调用将记录推送到数据库中的两个不同表中。如果失败了,我希望一切都能恢复。因此,如果
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道;
公共作废插入记录(列表记录){
用于(记录:记录){
插入A和B(记录);
}
}
@事务性(rollboor=Exception.class,propagation=propagation.REQUIRES\u NEW)
公共空白插入物A和B(记录){
插入体(记录);
插入数据库(记录);
}
@事务性(传播=传播。必需)
公共空白插入物(记录){
插入体(记录);
}
@事务性(传播=传播。必需)
公共无效插入B(记录){
dao.insertIntoB(记录);
}
公共无效setMyDAO(最终MyDAO){
this.dao=dao;
}
}
其中
MyDAO-dao
是一个使用mybatis映射到数据库并使用Spring注入进行设置的接口

现在,如果
insertIntoB
失败,那么从
insertIntoA
开始的所有内容仍然会被推送到数据库中。如何纠正这种行为

编辑:


我修改了这个类,以便更准确地描述我试图实现的目标。如果我直接运行
insertintoandb
,如果出现任何问题,回滚将起作用,但是如果我从
insertRecords
调用
insertintoandb
,如果出现任何问题,回滚将不起作用。

我认为您遇到的行为取决于您使用的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(使用SpringJDBCTemplate),那么还可以在spring级别调试SQL和事务

log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
仔细检查您的自动提交设置和特定于数据库的特殊情况(例如:大多数DDL将立即提交,尽管spring/hibernate这样做了,但您将无法回滚)

我找到了解决方案

显然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;
    }
}
公共类Foo{
公共作废插入记录(列表记录){
服务myService=新服务();
用于(记录:记录){
myService.insertIntoAAndB(记录);
}
}
}
公务舱服务{
MyDAO道;
@事务性(rollboor=Exception.class,propagation=propagation.REQUIRES\u NEW)
公共空白插入物A和B(记录){
插入体(记录);
插入数据库(记录);
}
@事务性(传播=传播。必需)
公共空白插入物(记录){
插入体(记录);
}
@事务性(传播=传播。必需)
公共无效插入B(记录){
dao.insertIntoB(记录);
}
公共无效setMyDAO(最终MyDAO){
this.dao=dao;
}
}

因为jdk不仅使用方法解析aop注释,还使用目标类解析注释。 例如,方法A使用@transactional,方法B调用方法A但不使用@transactional,当您使用反射调用方法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();
    }
}
public Object invoke(对象代理、方法、对象[]args)抛出Throwable{
......
//获取此方法的拦截链。
列表链=this.adviced.getInterceptorsAndDynamicInterceptionAdvice(方法,targetClass);
//检查我们是否有任何建议。如果没有,我们可以直接求助
//目标的反射调用,并避免创建MethodInvocation。
if(chain.isEmpty()){
//我们可以跳过创建MethodInvocation:直接调用目标
//请注意,最后一个调用程序必须是InvokeInterceptor,因此我们知道它是
//除了在目标上进行反射操作,没有热交换或代理。
retVal=AopUtils.invokeJoinpointUsingReflection(目标、方法、参数);
}
否则{
//我们需要创建一个方法调用。。。
调用=新的ReflectMethodInvocation(代理、目标、方法、参数、targetClass、链);
//通过拦截器链进入连接点。
retVal=invocation.procedure();
}
}

请发布您的spring配置。@gerrytan我编辑了我的问题,以便更准确地描述我在做什么。另请参见:;如果您使用AspectJ而不是Spring代理,Spring可以拦截来自同一类的方法调用(并且@Transnational也适用于私有方法)