@Transactional失效場(chǎng)景

上一篇:事務(wù)的兩種形式

@Transactional介紹

@Transactional注解底層使用的是動(dòng)態(tài)代理來(lái)進(jìn)行實(shí)現(xiàn)的

Transactional注解可以作用在接口、類、類方法

  • 作用接口: 不推薦,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效
  • 作用于類:表示該類的所有public方法都配置相同的事務(wù)屬性信息
  • 作用于類方法:只能用于public方法上。注意:如果類于類方法都配置了@Transactional,類方法事務(wù)會(huì)覆蓋類事務(wù)

propagation屬性

propagation代表事務(wù)的傳播行為,默認(rèn)值為Propagation.REQUIRED

  • Propagation.REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則新創(chuàng)建事務(wù)(也就是說(shuō)如果A方法和B方法都添加了注解,默認(rèn)傳播模式下,A方法調(diào)用B方法,會(huì)將兩個(gè)方法事務(wù)合并為一個(gè))
  • Propagation.SUPPORTS:如果當(dāng)前存在事務(wù),則加入,如果不存在,則以非事務(wù)形式運(yùn)行
  • Propagation.MANDATORY:如果當(dāng)前存在事務(wù),則加入事務(wù),如果不存在事務(wù),則拋異常
  • Propagation.REQUIRES_NEW:重新創(chuàng)建一個(gè)事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前事務(wù)(如果A方法默認(rèn)為Propagation.REQUIRED模式,B方法為Propagation.REQUIRES_NEW,在A方法中調(diào)用B方法,A方法拋出異常后,B方法不會(huì)回滾,因?yàn)镻ropagation.REQUIRES_NEW會(huì)暫停A方法的事務(wù))
  • Propagation.NOT_SUPPORTED:以非事務(wù)方法運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前事務(wù)
  • Propagation.NESTED :和 Propagation.REQUIRED 效果一樣

isolation屬性

isolation事務(wù)的隔離級(jí)別,默認(rèn)值為Isolation.DEFAULT

  • Isolation.DEFAULT:使用底層數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別
  • Isolation.READ_UNCOMMITTED
  • Isolation.READ_COMMITTED
  • Isolation.REPEATABLE_READ
  • Isolation.SERIALIZABLE

timeout屬性

timeout事務(wù)的超時(shí)時(shí)間,默認(rèn)值:-1,如果超過該時(shí)間限制事務(wù)還未完成,則自動(dòng)回滾

readOnly屬性

readOnly指定事務(wù)是否為只讀事務(wù),默認(rèn)值為false,它的存在是為了讓那些不需要事務(wù)的方法被排除掉,例如:讀取數(shù)據(jù),可以設(shè)置為readOnly為true

rollbackFor屬性

用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個(gè)異常類型

noRollbackFor屬性

指定的異常類型,不回滾事務(wù),可以指定多個(gè)異常類型

@Transactional失效場(chǎng)景

  1. @Transactional注解應(yīng)用在非public方法上


    image.png

    之所以會(huì)失效式因?yàn)樵赟pring AOP代理時(shí),如上圖所示 TransactionInterceptor (事務(wù)攔截器)在目標(biāo)方法執(zhí)行前后進(jìn)行攔截,DynamicAdvisedInterceptor(CglibAopProxy 的內(nèi)部類)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法會(huì)間接調(diào)用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,獲取Transactional 注解的事務(wù)配置信息。


    image.png
  2. propagation 配置錯(cuò)誤
    其中這三種會(huì)存在以非事務(wù)形式運(yùn)行
    TransactionDefinition.PROPAGATION_SUPPORTS、TransactionDefinition.PROPAGATION_NOT_SUPPORTED、
    TransactionDefinition.PROPAGATION_NEVER;
    Propagation.REQUIRES_NEW也會(huì)存在事務(wù)失效的情況,見上述Propagation.REQUIRES_NEW的舉例描述

  3. rollbackFor配置錯(cuò)誤


    image.png

Spring默認(rèn)拋出了未檢查unchecked異常(繼承自 RuntimeException 的異常)或者 Error才回滾事務(wù);其他異常不會(huì)觸發(fā)回滾事務(wù)。如果在事務(wù)中拋出其他類型的異常,但卻期望 Spring 能夠回滾事務(wù),就需要指定 rollbackFor屬性。
希望自定義的異??梢赃M(jìn)行回滾@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
若在目標(biāo)方法中拋出的異常是 rollbackFor 指定的異常的子類,事務(wù)同樣會(huì)回滾。

  1. 同一個(gè)類中方法間的調(diào)用
    A調(diào)用本類中的B方法(不論B時(shí)public或者private),A沒有聲明事務(wù),而B方法有。外部調(diào)用A方法后,方法B的事務(wù)不會(huì)起作用。
    這是由于使用Spring AOP代理造成的,因?yàn)橹挥挟?dāng)前事務(wù)方法被當(dāng)前類以外的代碼調(diào)用時(shí),才會(huì)由Spring生成的代理對(duì)象來(lái)管理。
//@Transactional 
@GetMapping("/test") 
private Integer A() throws Exception { 
    CityInfoDict cityInfoDict = new CityInfoDict(); 
    cityInfoDict.setCityName("2"); 
    /** * B 插入字段為 3的數(shù)據(jù) */ 
    this.insertB();
 /** * A 插入字段為 2的數(shù)據(jù) */ 
    int insert = cityInfoDictMapper.insert(cityInfoDict);
    return insert; 
} 
@Transactional() 
public Integer insertB() throws Exception {
    CityInfoDict cityInfoDict = new CityInfoDict();
    cityInfoDict.setCityName("3");
    cityInfoDict.setParentCityId(3);
    return cityInfoDictMapper.insert(cityInfoDict);
 }
  1. 異常被你的catch“吃了”
@Transactional 
private Integer A() throws Exception {
    int insert = 0; 
    try { 
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setCityName("2");
        cityInfoDict.setParentCityId(2); 
        /** * A 插入字段為 2的數(shù)據(jù) */
        insert = cityInfoDictMapper.insert(cityInfoDict);
        /** * B 插入字段為 3的數(shù)據(jù) */ 
        b.insertB(); 
    } catch (Exception e) {
        e.printStackTrace();
    }
 }

如果B方法發(fā)生了異常,而A方法catch住了B方法的異常,那么這個(gè)事務(wù)不能正?;貪L
會(huì)拋出:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

因?yàn)楫?dāng)B方法中拋出了一個(gè)異常后,B標(biāo)識(shí)當(dāng)前事務(wù)需要rollback。但是A中由于收到鋪貨了這個(gè)異常并進(jìn)行了處理,A任務(wù)當(dāng)前事務(wù)應(yīng)該可以正常commit。此時(shí)前后不一致,拋出UnexpectedRollbackException

Spring事務(wù)時(shí)在調(diào)用業(yè)務(wù)方法之前開始的,業(yè)務(wù)方法執(zhí)行完畢之后才執(zhí)行commit or rollback,事務(wù)是否執(zhí)行取決于是否拋出RuntimeException,如果拋出了并且未catch到的話,事務(wù)就會(huì)回滾。

  1. 數(shù)據(jù)庫(kù)不支持事務(wù)
    mysql數(shù)據(jù)庫(kù)默認(rèn)使用支持事務(wù)的innodb引擎,一旦切換為不支持事務(wù)的myisam,那么事務(wù)就會(huì)失效

原文鏈接:https://baijiahao.baidu.com/s?id=1661650900351466294&wfr=spider&for=pc

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容