久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

@Transactional 注解失效的原因有哪些

133次閱讀
沒有評論

共計 6171 個字符,預計需要花費 16 分鐘才能閱讀完成。

@Transactional 注解失效的原因有哪些,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

一、事務

事務管理在系統開發中是不可缺少的一部分,Spring 提供了很好事務管理機制,主要分為編程式事務和聲明式事務兩種。

編程式事務:是指在代碼中手動的管理事務的提交、回滾等操作,代碼侵入性比較強,如下示例:

try { //TODO something transactionManager.commit(status); } catch (Exception e) { transactionManager.rollback(status); throw new InvoiceApplyException(異常失敗  }

聲明式事務:基于 AOP 面向切面的,它將具體業務與事務處理部分解耦,代碼侵入性很低,所以在實際開發中聲明式事務用的比較多。聲明式事務也有兩種實現方式,一是基于 TX 和 AOP 的 xml 配置文件方式,二種就是基于 @Transactional 注解了。

@Transactional @GetMapping(/test) public String test() { int insert = cityInfoDictMapper.insert(cityInfoDict); }

二、@Transactional 介紹

1、@Transactional 注解可以作用于哪些地方?

@Transactional 可以作用在接口、類、類方法。

作用于類:當把 @Transactional 注解放在類上時,表示所有該類的 public 方法都配置相同的事務屬性信息。

作用于方法:當類配置了 @Transactional,方法也配置了 @Transactional,方法的事務會覆蓋類的事務配置信息。

作用于接口:不推薦這種使用方法,因為一旦標注在 Interface 上并且配置了 Spring AOP   使用 CGLib 動態代理,將會導致 @Transactional 注解失效

@Transactional @RestController @RequestMapping public class MybatisPlusController { @Autowired private CityInfoDictMapper cityInfoDictMapper; @Transactional(rollbackFor = Exception.class) @GetMapping(/test) public String test() throws Exception { CityInfoDict cityInfoDict = new CityInfoDict(); cityInfoDict.setParentCityId(2); cityInfoDict.setCityName(2  cityInfoDict.setCityLevel( 2  cityInfoDict.setCityCode( 2  int insert = cityInfoDictMapper.insert(cityInfoDict); return insert +   } }

2、@Transactional 注有哪些屬性?

propagation 屬性

propagation 代表事務的傳播行為,默認值為 Propagation.REQUIRED,其他的屬性信息如下:

Propagation.REQUIRED:如果當前存在事務,則加入該事務,如果當前不存在事務,則創建一個新的事務。(  也就是說如果 A 方法和 B 方法都添加了注解,在默認傳播模式下,A 方法內部調用 B 方法,會把兩個方法的事務合并為一個事務)

Propagation.SUPPORTS:如果當前存在事務,則加入該事務; 如果當前不存在事務,則以非事務的方式繼續運行。

Propagation.MANDATORY:如果當前存在事務,則加入該事務; 如果當前不存在事務,則拋出異常。

Propagation.REQUIRES_NEW:重新創建一個新的事務,如果當前存在事務,暫停當前的事務。(當類 A 中的 a   方法用默認 Propagation.REQUIRED 模式,類 B 中的 b 方法加上采用 Propagation.REQUIRES_NEW 模式,然后在 a 方法中調用  b 方法操作數據庫,然而 a 方法拋出異常后,b 方法并沒有進行回滾,因為 Propagation.REQUIRES_NEW 會暫停 a 方法的事務)

Propagation.NOT_SUPPORTED:以非事務的方式運行,如果當前存在事務,暫停當前的事務。

Propagation.NEVER:以非事務的方式運行,如果當前存在事務,則拋出異常。

Propagation.NESTED:和 Propagation.REQUIRED 效果一樣。

isolation 屬性

isolation:事務的隔離級別,默認值為 Isolation.DEFAULT。

Isolation.DEFAULT:使用底層數據庫默認的隔離級別。

Isolation.READ_UNCOMMITTED

Isolation.READ_COMMITTED

Isolation.REPEATABLE_READ

Isolation.SERIALIZABLE

timeout 屬性

timeout:事務的超時時間,默認值為 -1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。

readOnly 屬性

readOnly:指定事務是否為只讀事務,默認值為 false; 為了忽略那些不需要事務的方法,比如讀取數據,可以設置 read-only 為  true。

rollbackFor 屬性

rollbackFor:用于指定能夠觸發事務回滾的異常類型,可以指定多個異常類型。

noRollbackFor 屬性 **

noRollbackFor:拋出指定的異常類型,不回滾事務,也可以指定多個異常類型。

二、@Transactional 失效場景

接下來我們結合具體的代碼分析一下哪些場景下,@Transactional 注解會失效。

1、@Transactional 應用在非 public 修飾的方法上

如果 Transactional 注解應用在非 public 修飾的方法上,Transactional 將會失效。

在這里插入圖片描述

之所以會失效是因為在 Spring AOP 代理時,如上圖所示 TransactionInterceptor  (事務攔截器)在目標方法執行前后進行攔截,DynamicAdvisedInterceptor(CglibAopProxy 的內部類)的 intercept   方法或 JdkDynamicAopProxy 的 invoke 方法會間接調用  AbstractFallbackTransactionAttributeSource 的 computeTransactionAttribute   方法,獲取 Transactional 注解的事務配置信息。

protected TransactionAttribute computeTransactionAttribute(Method method, Class ?  targetClass) { // Don t allow no-public methods as required. if (allowPublicMethodsOnly()   !Modifier.isPublic(method.getModifiers())) { return null; }

此方法會檢查目標方法的修飾符是否為 public,不是 public 則不會獲取 @Transactional 的屬性配置信息。

注意:protected、private 修飾的方法上使用 @Transactional   注解,雖然事務無效,但不會有任何報錯,這是我們很容犯錯的一點。

2、@Transactional 注解屬性 propagation 設置錯誤

這種失效是由于配置錯誤,若是錯誤的配置以下三種 propagation,事務將不會發生回滾。

TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務; 如果當前沒有事務,則以非事務的方式繼續運行。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。

TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。

3、@Transactional 注解屬性 rollbackFor 設置錯誤

rollbackFor 可以指定能夠觸發事務回滾的異常類型。Spring 默認拋出了未檢查 unchecked 異常 (繼承自  RuntimeException 的異常) 或者 Error 才回滾事務; 其他異常不會觸發回滾事務。如果在事務中拋出其他類型的異常,但卻期望 Spring   能夠回滾事務,就需要指定 rollbackFor 屬性。

在這里插入圖片描述

//  希望自定義的異常可以進行回滾  @Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class

若在目標方法中拋出的異常是 rollbackFor 指定的異常的子類,事務同樣會回滾。Spring 源碼如下:

private int getDepth(Class ?  exceptionClass, int depth) { if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } // If we ve gone as far as we can go and haven t found it... if (exceptionClass == Throwable.class) { return -1; } return getDepth(exceptionClass.getSuperclass(), depth + 1); }

4、同一個類中方法調用,導致 @Transactional 失效

開發中避免不了會對同一個類里面的方法調用,比如有一個類 Test,它的一個方法 A,A 再調用本類的方法 B(不論方法 B 是用 public 還是 private 修飾),但方法 A 沒有聲明注解事務,而 B 方法有。則外部調用方法 A 之后,方法 B 的事務是不會起作用的。這也是經常犯錯誤的一個地方。

那為啥會出現這種情況? 其實這還是由于使用 Spring  AOP 代理造成的,因為只有當事務方法被當前類以外的代碼調用時,才會由 Spring 生成的代理對象來管理。

//@Transactional @GetMapping(/test) private Integer A() throws Exception { CityInfoDict cityInfoDict = new CityInfoDict(); cityInfoDict.setCityName(2  /** * B  插入字段為  3 的數據  */ this.insertB(); /** * A  插入字段為  2 的數據  */ 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); }

5、異常被你的 catch“吃了”導致 @Transactional 失效

這種情況是最常見的一種 @Transactional 注解失效場景

@Transactional private Integer A() throws Exception { int insert = 0; try { CityInfoDict cityInfoDict = new CityInfoDict(); cityInfoDict.setCityName(2  cityInfoDict.setParentCityId(2); /** * A  插入字段為  2 的數據  */ insert = cityInfoDictMapper.insert(cityInfoDict); /** * B  插入字段為  3 的數據  */ b.insertB(); } catch (Exception e) { e.printStackTrace(); } }

如果 B 方法內部拋了異常,而 A 方法此時 try catch 了 B 方法的異常,那這個事務還能正常回滾嗎?

答案:不能!

會拋出異常:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

因為當 ServiceB 中拋出了一個異常以后,ServiceB 標識當前事務需要 rollback。但是 ServiceA 中由于你手動的捕獲這個異常并進行處理,ServiceA 認為當前事務應該正常 commit。此時就出現了前后不一致,也就是因為這樣,拋出了前面的 UnexpectedRollbackException 異常。

spring 的事務是在調用業務方法之前開始的,業務方法執行完畢之后才執行 commit or  rollback,事務是否執行取決于是否拋出 runtime 異常。如果拋出 runtime exception   并在你的業務方法中沒有 catch 到的話,事務會回滾。

在業務方法中一般不需要 catch 異常,如果非要 catch 一定要拋出 throw new  RuntimeException(),或者注解中指定拋異常類型 @Transactional(rollbackFor=Exception.class),否則會導致事務失效,數據 commit 造成數據不一致,所以有些時候 try  catch 反倒會畫蛇添足。

6、數據庫引擎不支持事務

這種情況出現的概率并不高,事務能否生效數據庫引擎是否支持事務是關鍵。常用的 MySQL 數據庫默認使用支持事務的 innodb 引擎。一旦數據庫引擎切換成不支持事務的 myisam,那事務就從根本上失效了。

關于 @Transactional 注解失效的原因有哪些問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注丸趣 TV 行業資訊頻道了解更多相關知識。

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-03發表,共計6171字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 建德市| 阳曲县| 玛纳斯县| 乌苏市| 黎川县| 墨脱县| 龙井市| 克拉玛依市| 博罗县| 汕尾市| 苍梧县| 锡林浩特市| 芦溪县| 普安县| 辽阳县| 永城市| 伽师县| 罗平县| 称多县| 安西县| 安溪县| 舟山市| 富阳市| 怀仁县| 上栗县| 繁峙县| 海兴县| 雷波县| 光泽县| 呼和浩特市| 贺州市| 余姚市| 北流市| 山东| 聂荣县| 厦门市| 绥阳县| 永康市| 许昌县| 工布江达县| 铁岭县|