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

Spring事務(wù)@Transactional注解四種不生效案例場(chǎng)景分析

 更新時(shí)間:2022年07月21日 11:23:31   作者:skyarthur  
這篇文章主要為大家介紹了Spring事務(wù)@Transactional注解四種不生效的案例場(chǎng)景示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

在我們工作中,經(jīng)常會(huì)用到 @Transactional 聲明事務(wù),不正確的使用姿勢(shì)會(huì)導(dǎo)致注解失效,下面就來(lái)分析四種最常見(jiàn)的@Transactional事務(wù)不生效的 Case:

類內(nèi)部訪問(wèn):A 類的 a1 方法沒(méi)有標(biāo)注 @Transactional,a2 方法標(biāo)注 @Transactional,在 a1 里面調(diào)用 a2;

私有方法:將 @Transactional 注解標(biāo)注在非 public 方法上;

異常不匹配:@Transactional 未設(shè)置 rollbackFor 屬性,方法返回 Exception 等異常;

多線程:主線程和子線程的調(diào)用,線程拋出異常。

示例代碼

UserDao 接口,操作數(shù)據(jù)庫(kù);UserController 實(shí)現(xiàn)業(yè)務(wù)邏輯,聲明事務(wù),調(diào)用 UserController.testSuccess 方法,事務(wù)聲明生效

// 提供的接口
public interface UserDao {
    // select * from user_test where uid = "#{uid}"
    public MyUser selectUserById(Integer uid);
    // update user_test set uname =#{uname},usex = #{usex} where uid = #{uid}
    public int updateUser(MyUser user);
}
@Service
public class UserController {
    @Autowired
    private UserDao userDao;
    public void update(Integer id) {
        MyUser user = new MyUser();
        user.setUid(id);
        user.setUname("張三-testing");
        user.setUsex("女");
        userDao.updateUser(user);
    }
    public MyUser query(Integer id) {
        MyUser user = userDao.selectUserById(id);
        return user;
    }
    // 正常情況
    @Transactional(rollbackFor = Exception.class)
    public void testSuccess() throws Exception {
        Integer id = 1;
        MyUser user = query(id);
        System.out.println("原記錄:" + user);
        update(id);
        throw new Exception("事務(wù)生效");
    }
}

 為了快速說(shuō)明問(wèn)題,直接在controller中實(shí)現(xiàn)了業(yè)務(wù)邏輯和事務(wù)聲明,不代表生產(chǎn)環(huán)境中的代碼分層

1. 類內(nèi)部訪問(wèn)

在類 UserController 中新增一個(gè)方法 testInteralCall():

public void testInteralCall() throws Exception {
    testSuccess();
    throw new Exception("事務(wù)不生效:類內(nèi)部訪問(wèn)");
}

這里 testInteralCall() 沒(méi)有標(biāo)注 @Transactional,我們?cè)倏匆幌聹y(cè)試用例:

public static void main(String[] args) throws Exception {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserController uc = (UserController) applicationContext.getBean("userController");
    try {
        uc.testSuccess();
    } finally {
        MyUser user =  uc.query(1);
        System.out.println("修改后的記錄:" + user);
    }
}
// 輸出:
// 原記錄:MyUser(uid=1, uname=張三, usex=女)
// 修改后的記錄:MyUser(uid=1, uname=張三-testing, usex=女)

從上面的輸出可以看到,事務(wù)并沒(méi)有回滾,這個(gè)是什么原因呢?

因?yàn)?@Transactional 的工作機(jī)制是基于 AOP 實(shí)現(xiàn),AOP 是使用動(dòng)態(tài)代理實(shí)現(xiàn)的,如果通過(guò)代理直接調(diào)用 testSuccess(),通過(guò) AOP 會(huì)前后進(jìn)行增強(qiáng),增強(qiáng)的邏輯其實(shí)就是在 testSuccess() 的前后分別加上開(kāi)啟、提交事務(wù)的邏輯。

現(xiàn)在是通過(guò) testInteralCall() 去調(diào)用 testSuccess(),testSuccess() 前后不會(huì)進(jìn)行任何增強(qiáng)操作,也就是類內(nèi)部調(diào)用,不會(huì)通過(guò)代理方式訪問(wèn)。

2. 私有方法

在私有方法上,添加 @Transactional 注解也不會(huì)生效:

@Transactional(rollbackFor = Exception.class)
private void testPirvateMethod() throws Exception {
    Integer id = 1;
    MyUser user = query(id);
    System.out.println("原記錄:" + user);
    update(id);
    throw new Exception("測(cè)試事務(wù)生效");
}

直接使用時(shí),下面這種場(chǎng)景不太容易出現(xiàn),因?yàn)?IDEA 會(huì)有提醒,文案為: Methods annotated with '@Transactional' must be overridable,至于深層次的原理,源碼部分會(huì)給你解讀。

3. 異常不匹配

這里的 @Transactional 沒(méi)有設(shè)置 rollbackFor = Exception.class 屬性:

@Transactional
public void testExceptionNotMatch() throws Exception {
    Integer id = 1;
    MyUser user = query(id);
    System.out.println("原記錄:" + user);
    update(id);
    throw new Exception("事務(wù)不生效:異常不匹配");
}

@Transactional 注解默認(rèn)處理運(yùn)行時(shí)異常,即只有拋出運(yùn)行時(shí)異常時(shí),才會(huì)觸發(fā)事務(wù)回滾,否則并不會(huì)回滾,至于深層次的原理,源碼部分會(huì)給你解讀。

4. 多線程

父線程拋出異常

父線程拋出異常,子線程不拋出異常:

public void testSuccess() throws Exception {
    Integer id = 1;
    MyUser user = query(id);
    System.out.println("原記錄:" + user);
    update(id);
}
@Transactional(rollbackFor = Exception.class)
public void testMultThread() throws Exception {
    new Thread(new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            testSuccess();
        }
    }).start();
    throw new Exception("測(cè)試事務(wù)不生效");
}

父線程拋出線程,事務(wù)回滾,因?yàn)樽泳€程是獨(dú)立存在,和父線程不在同一個(gè)事務(wù)中,所以子線程的修改并不會(huì)被回滾

子線程拋出異常

父線程不拋出異常,子線程拋出異常:

public void testSuccess() throws Exception {
    Integer id = 1;
    MyUser user = query(id);
    System.out.println("原記錄:" + user);
    update(id);
    throw new Exception("測(cè)試事務(wù)不生效");
}
@Transactional(rollbackFor = Exception.class)
public void testMultThread() throws Exception {
    new Thread(new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            testSuccess();
        }
    }).start();
}

由于子線程的異常不會(huì)被外部的線程捕獲,所以父線程不拋異常,事務(wù)回滾沒(méi)有生效。

源碼解讀

@Transactional 執(zhí)行機(jī)制

我們只看最核心的邏輯,代碼中的 interceptorOrInterceptionAdvice 就是 TransactionInterceptor 的實(shí)例,入?yún)⑹?this 對(duì)象。

紅色方框有一段注釋,大致翻譯為 “它是一個(gè)攔截器,所以我們只需調(diào)用即可:在構(gòu)造此對(duì)象之前,將靜態(tài)地計(jì)算切入點(diǎn)。”

this 是 ReflectiveMethodInvocation 對(duì)象,成員對(duì)象包含 UserController 類、testSuccess() 方法、入?yún)⒑痛韺?duì)象等。

進(jìn)入 invoke() 方法后:

前方高能?。?!這里就是事務(wù)的核心邏輯,包括判斷事務(wù)是否開(kāi)啟、目標(biāo)方法執(zhí)行、事務(wù)回滾、事務(wù)提交。

private 導(dǎo)致事務(wù)不生效原因

在上面這幅圖中,第一個(gè)紅框區(qū)域調(diào)用了方法 getTransactionAttribute(),主要是為了獲取 txAttr 變量,它是用于讀取 @Transactional 的配置,如果這個(gè) txAttr = null,后面就不會(huì)走事務(wù)邏輯,我們看一下這個(gè)變量的含義:

我們直接進(jìn)入 getTransactionAttribute(),重點(diǎn)關(guān)注獲取事務(wù)配置的方法。

前方高能!?。∵@里就是 private 導(dǎo)致事務(wù)不生效的原因所在,allowPublicMethodsOnly() 一直返回 false,所以重點(diǎn)只關(guān)注 isPublic() 方法。

異常不匹配原因

我們繼續(xù)回到事務(wù)的核心邏輯,因?yàn)橹鞣椒⊕伋?Exception() 異常,進(jìn)入事務(wù)回滾的邏輯:

進(jìn)入 rollbackOn() 方法,判斷該異常是否能進(jìn)行回滾,這個(gè)需要判斷主方法拋出的 Exception() 異常,是否在 @Transactional 的配置中:

我們進(jìn)入 getDepth() 看一下異常規(guī)則匹配邏輯,因?yàn)槲覀儗?duì) @Transactional 配置了 rollbackFor = Exception.class,所以能匹配成功:

示例中的 winner 不為 null,所以會(huì)跳過(guò)下面的環(huán)節(jié)。但是當(dāng) winner = null 時(shí),也就是沒(méi)有設(shè)置 rollbackFor 屬性時(shí),會(huì)走默認(rèn)的異常捕獲方式。

前方高能?。。∵@里就是異常不匹配原因的原因所在,我們看一下默認(rèn)的異常捕獲方式:

是不是豁然開(kāi)朗,當(dāng)沒(méi)有設(shè)置 rollbackFor 屬性時(shí),默認(rèn)只對(duì) RuntimeException 和 Error 的異常執(zhí)行回滾。

以上就是Spring事務(wù)@Transactional注解的四種不生效案例場(chǎng)景分析的詳細(xì)內(nèi)容,更多關(guān)于Spring事務(wù)@Transactional不生效的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java線程中synchronized的用法與原理解析

    Java線程中synchronized的用法與原理解析

    這篇文章主要介紹了Java線程中synchronized的用法與原理解析,只要有線程,就會(huì)有并發(fā)的現(xiàn)象,也同時(shí)會(huì)產(chǎn)生數(shù)據(jù)不一致,那么對(duì)于需要使用同一個(gè)數(shù)據(jù)的兩個(gè)線程,就會(huì)產(chǎn)生沖突,那么就引出了鎖的概念,本篇會(huì)針對(duì)性的說(shuō)下synchronized這個(gè)關(guān)鍵字,需要的朋友可以參考下
    2024-01-01
  • Mybatis?saveAndUpdate空值不更新問(wèn)題及解決

    Mybatis?saveAndUpdate空值不更新問(wèn)題及解決

    這篇文章主要介紹了Mybatis?saveAndUpdate空值不更新問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java面試題之MD5加密的安全性詳解

    Java面試題之MD5加密的安全性詳解

    MD5 是 Message Digest Algorithm 的縮寫(xiě),譯為信息摘要算法,它是 Java 語(yǔ)言中使用很廣泛的一種加密算法。本文將通過(guò)示例討論下MD5的安全性,感興趣的可以了解一下
    2022-10-10
  • Java集合中的LinkedHashMap使用解析

    Java集合中的LinkedHashMap使用解析

    這篇文章主要介紹了Java集合中的LinkedHashMap使用解析,LinkedHashMap是繼承于HashMap的,所以它的很多屬性和方法都是HashMap中的,那么它是怎么實(shí)現(xiàn)有序存儲(chǔ)的呢,需要的朋友可以參考下
    2023-12-12
  • Java實(shí)現(xiàn)簡(jiǎn)單掃雷程序

    Java實(shí)現(xiàn)簡(jiǎn)單掃雷程序

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單掃雷程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • SpringBoot如何實(shí)現(xiàn)同域SSO(單點(diǎn)登錄)

    SpringBoot如何實(shí)現(xiàn)同域SSO(單點(diǎn)登錄)

    單點(diǎn)登錄(SingleSignOn,SSO),就是通過(guò)用戶的一次性鑒別登錄。即在多個(gè)應(yīng)用系統(tǒng)中,只需要登錄一次,就可以訪問(wèn)其他相互信任的應(yīng)用系統(tǒng),本文將介紹SpringBoot如何實(shí)現(xiàn)同域SSO(單點(diǎn)登錄)
    2021-05-05
  • SpringCloud Gateway HttpWebHandlerAdapter鏈路調(diào)用請(qǐng)求流程介紹

    SpringCloud Gateway HttpWebHandlerAdapter鏈路調(diào)用請(qǐng)求流程介

    Spring Cloud Gateway旨在為微服務(wù)架構(gòu)提供一種簡(jiǎn)單有效的、統(tǒng)一的 API 路由管理方式。Spring Cloud Gateway 作為 Spring Cloud 生態(tài)系中的網(wǎng)關(guān),它不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全、監(jiān)控/埋點(diǎn)和限流等
    2022-10-10
  • Springboot Apollo配置yml的問(wèn)題及解決方案

    Springboot Apollo配置yml的問(wèn)題及解決方案

    這篇文章主要介紹了Springboot Apollo配置yml的問(wèn)題及解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件

    Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件

    這篇文章主要為大家詳細(xì)介紹了Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • JAVA自定義注解實(shí)現(xiàn)接口/ip限流的示例代碼

    JAVA自定義注解實(shí)現(xiàn)接口/ip限流的示例代碼

    本文主要介紹了JAVA自定義注解實(shí)現(xiàn)接口/ip限流的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論