簡體   English   中英

Java Spring @Transactional方法沒有按預期回滾

[英]Java Spring @Transactional method not rolling back as expected

下面是我正在嘗試做的快速概述。 我想從一個方法調用將記錄推送到數據庫中的兩個不同的表。 如果有任何失敗,我希望一切都回滾。 因此,如果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是使用MyDAO dao映射到數據庫的接口,使用Spring注入設置。

現在,如果insertIntoB失敗, insertIntoB所有insertIntoA仍然會被推送到數據庫。 我該如何糾正這種行為?

編輯:

我修改了這個類,以便更准確地描述我想要實現的目標。 如果我直接運行insertIntoAAndB ,如果有任何問題,回滾就會起作用,但是如果我從insertRecords調用insertIntoAAndB ,則如果出現任何問題,則回滾不起作用。

我找到了解決方案!

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

我認為您遇到的行為取決於您正在使用的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

仔細檢查你的自動提交設置和數據庫特定的peciular(例如:大多數DDL將立即進行調試,盡管spring / hibernate這樣做,你將無法回滾)

僅僅因為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();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM