亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring?@Transactional事務(wù)失效的原因分析

 更新時間:2022年09月15日 09:07:54   作者:Java中文社群  
一個程序中不可能沒有事務(wù),Spring中,事務(wù)的實現(xiàn)方式分為兩種:編程式事務(wù)和聲明式事務(wù)。日常項目中,我們都會使用聲明式事務(wù)?@Transactional來實現(xiàn)事務(wù),本文來和大家聊聊什么情況會導(dǎo)致@Transactional事務(wù)失效

前言

一個程序中不可能沒有事務(wù),而 Spring 中,事務(wù)的實現(xiàn)方式分為兩種:編程式事務(wù)和聲明式事務(wù),又因為編程式事務(wù)實現(xiàn)相對麻煩,而聲明式事務(wù)實現(xiàn)極其簡單,所以在日常項目中,我們都會使用聲明式事務(wù) @Transactional 來實現(xiàn)事務(wù)。

@Transactional 使用極其簡單,只需要在類上或方法上添加 @Transactional 關(guān)鍵字,就可以實現(xiàn)事務(wù)的自動開啟、提交或回滾了,它的基礎(chǔ)用法如下:

@Transactional 
@RequestMapping("/add")
public int add(UserInfo userInfo) {
    int result = userService.add(userInfo);
    return result;
}

@Transactional 執(zhí)行流程

@Transactional 會在方法執(zhí)行前,會自動開啟事務(wù);在方法成功執(zhí)行完,會自動提交事務(wù);如果方法在執(zhí)行期間,出現(xiàn)了異常,那么它會自動回滾事務(wù)。

然而,就是看起來極其簡單的 @Transactional,卻隱藏著一些“坑”,這些坑就是我們今天要講的主題:導(dǎo)致 @Transactional 事務(wù)失效的常見場景有哪些?

在開始之前,我們先要明確一個定義,什么叫做“失效”?

本文中的“失效”指的是“失去(它的)功效”,也就是當(dāng) @Transactional 不符合我們預(yù)期的結(jié)果時,我們就可以說 @Transactional 失效了。

那 @Transactional 失效的場景有哪些呢?接下來我們一一來看。

1.非 public 修飾的方法

當(dāng) @Transactional 修飾的方法為非 public 時,事務(wù)就失效了,比如以下代碼當(dāng)遇到異常之后,不能自動實現(xiàn)回滾:

@RequestMapping("/save")
int save(UserInfo userInfo) {
    // 非空效驗
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    // 執(zhí)行添加操作
    int result = userService.save(userInfo);
    System.out.println("add 受影響的行數(shù):" + result);
    int num = 10 / 0; // 此處設(shè)置一個異常
    return result;
}

以上程序的運行結(jié)果如下:

當(dāng)程序出現(xiàn)運行時異常時,我們預(yù)期的結(jié)果是事務(wù)應(yīng)該實現(xiàn)自動回滾,也就是添加用戶失敗,然而當(dāng)我們查詢數(shù)據(jù)庫時,卻發(fā)現(xiàn)事務(wù)并未執(zhí)行回滾操作,數(shù)據(jù)庫的數(shù)據(jù)如下圖所示:

2.timeout 超時

當(dāng)在 @Transactional 上,設(shè)置了一個較小的超時時間時,如果方法本身的執(zhí)行時間超過了設(shè)置的 timeout 超時時間,那么就會導(dǎo)致本來應(yīng)該正常插入數(shù)據(jù)的方法執(zhí)行失敗,示例代碼如下:

@Transactional(timeout = 3) // 超時時間為 3s
@RequestMapping("/save")
int save(UserInfo userInfo) throws InterruptedException {
    // 非空效驗
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    int result = userService.save(userInfo);
    return result;
}

UserService 的 save 方法實現(xiàn)如下:

public int save(UserInfo userInfo) throws InterruptedException {
    // 休眠 5s
    TimeUnit.SECONDS.sleep(5);
    int result = userMapper.add(userInfo);
    return result;
}

以上程序的運行結(jié)果如下:

數(shù)據(jù)庫沒有正確的插入數(shù)據(jù),如下圖所示:

3.代碼中有 try/catch

在前面 @Transactional 的執(zhí)行流程中,我們提到:當(dāng)方法中出現(xiàn)了異常之后,事務(wù)會自動回滾。然而,如果在程序中加了 try/catch 之后,@Transactional 就不會自動回滾事務(wù)了,示例代碼如下:

@Transactional
@RequestMapping("/save")
public int save(UserInfo userInfo) throws InterruptedException {
    // 非空效驗
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    int result = userService.save(userInfo);
    try {
        int num = 10 / 0; // 此處設(shè)置一個異常
    } catch (Exception e) {
    }
    return result;
}

以上程序的運行結(jié)果如下:

此時,查詢數(shù)據(jù)庫我們發(fā)現(xiàn),程序并沒有執(zhí)行回滾操作,數(shù)據(jù)庫中被成功的添加了一條數(shù)據(jù),如下圖所示:

4.調(diào)用類內(nèi)部 @Transactional 方法

當(dāng)調(diào)用類內(nèi)部的 @Transactional 修飾的方法時,事務(wù)是不會生效的,示例代碼如下:

@RequestMapping("/save")
public int saveMappping(UserInfo userInfo) {
    return save(userInfo);
}
@Transactional
public int save(UserInfo userInfo) {
    // 非空效驗
    if (userInfo == null ||
        !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword()))
        return 0;
    int result = userService.save(userInfo);
    int num = 10 / 0; // 此處設(shè)置一個異常
    return result;
}

以上代碼我們在添加方法 save 中添加了 @Transactional 聲明式事務(wù),并且添加了異常代碼,我們預(yù)期的結(jié)果是程序出現(xiàn)異常,事務(wù)進(jìn)行自動回滾,以上程序的執(zhí)行結(jié)果如下:

然而,當(dāng)我們查詢數(shù)據(jù)庫時發(fā)現(xiàn),程序執(zhí)行并不符合我們的預(yù)期,添加的數(shù)據(jù)并沒有進(jìn)行自動回滾操作,如下圖所示:

5.數(shù)據(jù)庫不支持事務(wù)

我們程序中的 @Transactional 只是給調(diào)用的數(shù)據(jù)庫發(fā)送了:開始事務(wù)、提交事務(wù)、回滾事務(wù)的指令,但是如果數(shù)據(jù)庫本身不支持事務(wù),比如 MySQL 中設(shè)置了使用 MyISAM 引擎,那么它本身是不支持事務(wù)的,這種情況下,即使在程序中添加了 @Transactional 注解,那么依然不會有事務(wù)的行為,這就是巧婦也難為無米之炊吧。

總結(jié)

當(dāng)聲明式事務(wù) @Transactional 遇到以下場景時,事務(wù)會失效:

  • 非 public 修飾的方法;
  • timeout 設(shè)置過??;
  • 代碼中使用 try/catch 處理異常;
  • 調(diào)用類內(nèi)部 @Transactional 方法;
  • 數(shù)據(jù)庫不支持事務(wù)。

到此這篇關(guān)于Spring @Transactional事務(wù)失效的原因分析的文章就介紹到這了,更多相關(guān)Spring @Transactional事務(wù)失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java使用UDP實現(xiàn)點對點通信

    java使用UDP實現(xiàn)點對點通信

    這篇文章主要為大家詳細(xì)介紹了java使用UDP實現(xiàn)點對點通信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • spring boot基于DRUID實現(xiàn)數(shù)據(jù)源監(jiān)控過程解析

    spring boot基于DRUID實現(xiàn)數(shù)據(jù)源監(jiān)控過程解析

    這篇文章主要介紹了spring boot基于DRUID實現(xiàn)數(shù)據(jù)源監(jiān)控過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • Java8 Map中新增的方法使用總結(jié)

    Java8 Map中新增的方法使用總結(jié)

    這篇文章主要介紹了Java8 Map中新增的方法使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • jpa?EntityManager?復(fù)雜查詢實例

    jpa?EntityManager?復(fù)雜查詢實例

    這篇文章主要介紹了jpa?EntityManager?復(fù)雜查詢實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • JavaMail入門教程之發(fā)送郵件(3)

    JavaMail入門教程之發(fā)送郵件(3)

    這篇文章主要為大家詳細(xì)介紹了JavaMail入門教程之發(fā)送郵件的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Mybatis MapperScannerConfigurer自動掃描Mapper接口生成代理注入到Spring的方法

    Mybatis MapperScannerConfigurer自動掃描Mapper接口生成代理注入到Spring的方法

    這篇文章主要給大家介紹了關(guān)于Mybatis MapperScannerConfigurer自動掃描將Mapper接口生成代理注入到Spring的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2019-03-03
  • java面向?qū)ο罄^承與多態(tài)介紹

    java面向?qū)ο罄^承與多態(tài)介紹

    大家好,本篇文章主要講的是java面向?qū)ο罄^承與多態(tài)介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • Java創(chuàng)建線程池為什么一定要用ThreadPoolExecutor

    Java創(chuàng)建線程池為什么一定要用ThreadPoolExecutor

    本文介紹了Java創(chuàng)建線程池為什么一定要用ThreadPoolExecutor,手動方式使用ThreadPoolExecutor創(chuàng)建線程池和使用Executors執(zhí)行器自動創(chuàng)建線程池,下文更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-05-05
  • java中timer的schedule和scheduleAtFixedRate方法區(qū)別詳解

    java中timer的schedule和scheduleAtFixedRate方法區(qū)別詳解

    這篇文章主要為大家詳細(xì)介紹了java中timer的schedule和scheduleAtFixedRate方法區(qū)別,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • 淺談SpringBoot優(yōu)化技巧

    淺談SpringBoot優(yōu)化技巧

    這篇文章主要介紹了淺談SpringBoot優(yōu)化技巧,需要的朋友可以參考下。
    2017-09-09

最新評論