最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring事務失效的9大場景與解決方法

 更新時間:2025年04月30日 09:29:48   作者:寫bug寫bug  
在日常開發(fā)中,我們經(jīng)常使用Spring事務,這篇文章主要來和大家聊聊Spring事務失效的 9 種場景,并提供了相應的解決方法,有需要的小伙伴可以參考一下

前言

在日常開發(fā)中,我們經(jīng)常使用Spring事務。最近,一個朋友去面試,被問到了這樣一個面試題:在什么情況下,Spring 事務會失效?

今天,我將和大家聊聊Spring事務失效的 9 種場景。

1. 拋出檢查異常(checked exceptions)

例如,你的事務控制代碼如下:

@Transactional
public void transactionTest() throws IOException {
    User user = new User();
    UserService.insert(user);
    throw new IOException();
}

如果沒有特別指定@Transactional,Spring 默認只會在遇到運行時異常RuntimeException或錯誤時回滾,而檢查異常如IOException不會觸發(fā)回滾。

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

解決方案:

知道原因后,解決方案也很簡單。配置rollbackFor屬性,例如:@Transactional(rollbackFor = Exception.class)

@Transactional(rollbackFor = Exception.class)
public void transactionTest() throws IOException {
    User user = new User();
    UserService.insert(user);
    throw new IOException();
}

2. 業(yè)務方法本身捕獲并處理了異常

@Transactional(rollbackFor = Exception.class)
public void transactionTest() {
    try {
        User user = new User();
        UserService.insert(user);
        int i = 1 / 0;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在這個場景中,事務失效的原因也很簡單。Spring 是否回滾事務取決于你是否拋出了異常。如果你自己捕獲了異常,Spring 就無法處理事務了。

看了上面的代碼,你可能會覺得這么簡單的問題,自己不可能犯這種低級錯誤。但我想告訴你,我身邊幾乎有一半的人都曾因此困擾過。

在編寫業(yè)務代碼時,代碼可能會更復雜,有很多嵌套的方法。稍不注意,就很容易觸發(fā)這個問題。舉個簡單的例子,假設你有一個審計功能,每次方法執(zhí)行完后,將審計結果保存到數(shù)據(jù)庫中。那么代碼可能會寫成這樣:

@Service
public class TransactionService {
    @Transactional(rollbackFor = Exception.class)
    public void transactionTest() throws IOException {
        User user = new User();
        UserService.insert(user);
        throw new IOException();
    }
}

下面的切面會作用于TransactionService

@Component
publicclass AuditAspect {
    @Autowired
    private AuditService auditService;

    @Around(value = "execution (* com.dylan.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            Audit audit = new Audit();
            Signature signature = pjp.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            String[] strings = methodSignature.getParameterNames();
            audit.setMethod(signature.getName());
            audit.setParameters(strings);
            Object proceed = pjp.proceed();
            audit.success(true);
            return proceed;
        } catch (Throwable e) {
            log.error("{}", e);
            audit.success(false);
        }
        auditService.save(audit);
        returnnull;
    }
}

在上面的例子中,如果程序執(zhí)行異常,事務也會失效。原因是Spring的事務切面優(yōu)先級最低。如果異常被切面捕獲,Spring 自然無法正確處理事務,因為事務管理器無法捕獲到異常。

解決方案:

只需移除try-catch。雖然我們知道在處理事務時,業(yè)務代碼不能自己捕獲異常,但只要代碼變得復雜,我們很容易不小心犯錯。

3. 同一個類中的方法調(diào)用

@Service
publicclass DefaultTransactionService implements Service {

    public void saveUser() throws Exception {
        // do something
        doInsert();
    }

    @Transactional(rollbackFor = Exception.class)
    public void doInsert() throws IOException {
        User user = new User();
        UserService.insert(user);
        thrownew IOException();
    }
}

這也是一個容易出錯的場景。事務失效的原因也很簡單。因為 Spring 的事務管理功能是通過動態(tài)代理實現(xiàn)的,而 Spring 默認使用 JDK 動態(tài)代理,JDK 動態(tài)代理通過接口實現(xiàn),并通過反射調(diào)用目標類。簡單理解,在saveUser()方法中,調(diào)用this.doInsert()時,this是真實對象,因此會直接執(zhí)行doInsert的業(yè)務邏輯,而不是代理邏輯,從而導致事務失效。

解決方案:

方案 1:直接在saveUser方法上添加@Transactional注解。

方案 2:可以將這兩個方法拆分到不同的類中。

方案 3:不使用注解實現(xiàn)事務,而是使用編程式事務來包裹需要開啟事務的代碼塊。例如:transactionTemplate.execute()。

public void doInsert() throws IOException {
    transactionTemplate.execute(() -> {
        User user = new User();
        UserService.insert(user);
        throw new IOException();
    });
}

4. 方法使用了final或static關鍵字

如果 Spring 使用 Cglib 代理實現(xiàn)(當你的代理類沒有實現(xiàn)接口時),而你的業(yè)務方法恰好使用了finalstatic關鍵字,那么事務控制也會失效。因為 Cglib 使用字節(jié)碼增強技術生成被代理類的子類,并重寫被代理類的方法來實現(xiàn)代理。如果被代理的方法使用了finalstatic關鍵字,子類就無法重寫被代理的方法。

如果 Spring 使用 JDK 動態(tài)代理實現(xiàn),JDK 動態(tài)代理是基于接口實現(xiàn)的,那么被finalstatic修飾的方法也無法被代理。

總之,如果方法連代理都沒有,那么事務回滾肯定無法實現(xiàn)。

解決方案:

盡量移除方法上的finalstatic關鍵字。

5. 方法不是public

如果方法不是public,Spring 事務也會失效,因為在 Spring 事務管理的源碼AbstractFallbackTransactionAttributeSource中,computeTransactionAttribute()方法會判斷目標方法是否是public。如果不是public,則返回null。

// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    return null;
}

解決方案:

將當前方法的訪問級別改為public。

6. 傳播機制使用不當

Spring事務的傳播機制指的是當多個事務方法相互調(diào)用時,事務應該如何傳播的策略。Spring提供了七種事務傳播機制:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEWNOT_SUPPORTED、NEVER、NESTED。如果你不了解這些傳播策略的原理,很容易導致事務失效。

@Service
publicclass TransactionsService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private AddressMapper addressMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void doInsert(User user, Address address) throws Exception {
        // do something
        userMapper.insert(user);
        saveAddress(address);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveAddress(Address address) {
        // do something
        addressMapper.insert(address);
    }
}

在上面的例子中,如果用戶插入失敗,不會導致saveAddress()回滾,因為這里使用的傳播機制是REQUIRES_NEW。REQUIRES_NEW的原理是,如果當前方法沒有事務,則創(chuàng)建一個新事務。如果當前方法已經(jīng)有事務,則掛起當前事務并創(chuàng)建一個新事務。父事務會等到當前事務完成后才提交。如果父事務發(fā)生異常,不會影響子事務的提交。

解決方案:

將事務傳播策略改為默認值REQUIRED。REQUIRED的原理是,如果當前有事務,則加入該事務。如果沒有事務,則創(chuàng)建一個新事務。父事務和被調(diào)用的事務處于同一個事務中。即使被調(diào)用的事務捕獲了異常,整個事務仍然會回滾。

7. 沒有被 Spring 管理

// @Service
public class OrderServiceImpl implements OrderService {
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}

如果此時@Service注解被注釋掉,這個類就不會被 Spring 加載為 Bean,那么這個類就不會被 Spring 管理,事務自然也會失效。

解決方案:

確保每個使用事務注解的Service都被 Spring 管理。

8. 多線程調(diào)用

@Service
publicclass UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;

    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        new Thread(() -> {
            try {
                test();
            } catch (Exception e) {
                roleService.doOtherThing();
            }
        }).start();
    }
}

@Service
publicclass RoleService {

    @Transactional
    public void doOtherThing() {
        try {
            int i = 1 / 0;
            System.out.println("save role table data");
        } catch (Exception e) {
            thrownew RuntimeException();
        }
    }
}

我們可以看到,在事務方法add中,調(diào)用了事務方法doOtherThing,但doOtherThing是在另一個線程中被調(diào)用的。

這會導致兩個方法不在同一個線程中,獲取的數(shù)據(jù)庫連接也不同,因此是兩個不同的事務。如果在doOtherThing方法中拋出異常,add方法是不可能回滾的。

我們所說的同一個事務,實際上指的是同一個數(shù)據(jù)庫連接。只有在同一個數(shù)據(jù)庫連接下,才能同時提交和回滾。如果在不同的線程中,獲取的數(shù)據(jù)庫連接肯定不同,因此它們是不同的事務。

解決方案:

這有點像分布式事務。盡量確保在同一個事務中處理。

9. 沒有配置開啟事務

如果在項目中沒有配置 Spring 的事務管理器,即使使用了 Spring 的事務管理功能,Spring 的事務也不會生效。例如,如果你是一個 Spring Boot 項目,并且沒有在 Spring Boot 項目中配置以下代碼:

@EnableTransactionManagement

解決方案:

確保在項目中正確配置了事務管理器。

總結

本文簡要闡述了 Spring 事務的實現(xiàn)原理,并列出了 9 種 Spring 事務失效的場景。相信很多朋友可能都遇到過這些問題。文章也詳細解釋了失效的原因,希望大家對 Spring 事務有新的理解。

到此這篇關于Spring事務失效的9大場景與解決方法的文章就介紹到這了,更多相關Spring事務失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • springboot快速集成mybatis-plus的詳細教程

    springboot快速集成mybatis-plus的詳細教程

    這篇文章主要介紹了springboot快速集成mybatis-plus的教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Java如何將String轉換成json對象或json數(shù)組

    Java如何將String轉換成json對象或json數(shù)組

    這篇文章主要介紹了Java如何將String轉換成json對象或json數(shù)組,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java二叉搜索樹基礎原理與實現(xiàn)方法詳解

    Java二叉搜索樹基礎原理與實現(xiàn)方法詳解

    這篇文章主要介紹了Java二叉搜索樹基礎原理與實現(xiàn)方法,結合圖文與實例形式詳細分析了Java二叉搜索樹的基本概念、原理、實現(xiàn)方法與操作注意事項,需要的朋友可以參考下
    2020-03-03
  • SpringBoot 跨域問題的解決方案

    SpringBoot 跨域問題的解決方案

    這篇文章主要介紹了SpringBoot 跨域問題的解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-06-06
  • 初步了解javafx

    初步了解javafx

    這篇文章主要介紹了初步了解javafx,具有一定借鑒價值,需要的朋友可以參考下
    2017-12-12
  • Mybatis中通過generator生成mapper、Dao、mapper.xml的方法

    Mybatis中通過generator生成mapper、Dao、mapper.xml的方法

    這篇文章主要介紹了Mybatis中通過generator生成mapper、Dao、mapper.xml的方法,需要的朋友可以參考下
    2017-01-01
  • java異常處理詳細介紹及實例

    java異常處理詳細介紹及實例

    這篇文章主要介紹了java異常處理詳細介紹及實例的相關資料,本文對java異常進行了知識層次的總結,需要的朋友可以參考下
    2017-04-04
  • java中的轉義字符介紹

    java中的轉義字符介紹

    普通的轉義字符序列和八進制轉義字符都比Unicode轉義字符要好得多,因為與Unicode轉義字符不同,轉義字符序列是在程序被解析為各種符號之后被處理的
    2013-09-09
  • Java 中的 print() 和 println()

    Java 中的 print() 和 println()

    這篇文章主要介紹了Java 中的 print() 和 println(),Java 中的 print()和println()方法用于在控制臺上顯示文本,下面來看看它們之間到底有什么區(qū)別吧,需要的朋友也可以參考一下
    2021-11-11
  • MyBatis的嵌套查詢解析

    MyBatis的嵌套查詢解析

    本篇文章主要介紹了MyBatis的嵌套查詢解析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05

最新評論

威信县| 黎川县| 个旧市| 天长市| 江津市| 盈江县| 申扎县| 都安| 太仓市| 德清县| 石家庄市| 澄城县| 宁乡县| 平安县| 隆子县| 包头市| 洞头县| 西林县| 东乌珠穆沁旗| 烟台市| 自贡市| 平罗县| 镇江市| 余干县| 通城县| 申扎县| 麻江县| 田东县| 永嘉县| 宁强县| 台湾省| 南投县| 个旧市| 鸡东县| 平塘县| 扬中市| 汉源县| 固原市| 平舆县| 尼玛县| 长宁区|