日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring 事务传播行为以及失效原因

發布時間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring 事务传播行为以及失效原因 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天在查看以前寫的代碼時,看到了事務的使用,感覺自己對這一塊并不是特別清晰,所以就系統的學習了一下。在學習過程中發現很多地方自己以前理解的還是有點不對,所以記錄一下學習筆記,希望幫助到大家。

一、事務傳播行為

備注:因為除了PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED,其他的都不是特別難以理解,所以我這里就只對這兩個做了一下代碼實例。

當事務方法被另外一個事務方法調用時,必須指定事務應該如何傳播,例如,方法可能繼續在當前事務中執行,也可以開啟一個新的事務,在自己的事務中執行。
聲明式事務的傳播行為可以通過 @Transactional 注解中的 propagation 屬性來定義,比如說:

@Transactional(propagation = Propagation.REQUIRED) public void savePosts(PostsParam postsParam) { }

TransactionDefinition 一共定義了 7 種事務傳播行為,其中PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW 兩種傳播行為是比較常用的。

PROPAGATION_REQUIRED

?這也是 @Transactional 默認的事務傳播行為,指的是如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。更確切地意思是:如果外部方法沒有開啟事務的話,Propagation.REQUIRED 修飾的內部方法會開啟自己的事務,且開啟的事務相互獨立,互不干擾。如果外部方法開啟事務并且是 Propagation.REQUIRED 的話,所有 Propagation.REQUIRED 修飾的內部方法和外部方法均屬于同一事務 ,只要一個方法回滾,整個事務都需要回滾。也就是說如果a方法和b方法都添加了注解,在默認傳播模式下,a方法內部調用b方法,會把兩個方法的事務合并為一個事務。

PROPAGATION_REQUIRES_NEW

創建一個新的事務,如果當前存在事務,則把當前事務掛起。也就是說不管外部方法是否開啟事務,Propagation.REQUIRES_NEW 修飾的內部方法都會開啟自己的事務。舉例來說:

@ResourceApplicationContext applicationContext;@Resourceprivate IUserService userService;@GetMapping("insert")@Transactional(rollbackFor = Exception.class)public Result insertUser(){User user = new User();user.setPhone("11111801061").setNickName("111");userService.save(user);UserController bean = applicationContext.getBean(UserController.class);bean.saveAnother();int k = 10/0;return Result.ok();}@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)public void saveAnother() {User user = new User();user.setPhone("11111801062").setNickName("222");userService.save(user);}

?在上面這個例子中,首先是insertUser()方法會開啟一個事務,然后在調用到saveAnother()方法時,因為該方法的事務行為是Propagation.REQUIRES_NEW,所以它會在新開啟一個事務,并且將原來的事務掛起。當saveAnother()方法的事務結束以后,insertUser()方法的事務恢復開始繼續執行。

在上面的例子中,因為saveAnother()方法是獨自開啟了一個新的事務,所以它并不受原來事務的影響,它在事務提交以后才會恢復insertUser()的事務,所以在insertUser()中雖然拋出了異常,但是此時saveAnother()事務早已提交,所以它并不受insertUser()中拋出異常的影響。

但是如果反過來,把代碼改成下面這種情況

?那么此時,因為saveAnother()在執行完成以后會將異常拋給insertUser(),所以insertUser()會將自己的事務進行回滾。這也就是很多博客說的A不會影響B但是B會影響A。

PROPAGATION_NESTED

如果當前存在事務,就在當前事務內執行;否則,就執行與 PROPAGATION_REQUIRED 類似的操作。我們用下面的例子來說明:

@GetMapping("insert")@Transactional(rollbackFor = Exception.class)public Result insertUser(){User user = new User();user.setPhone("11111801061").setNickName("111");userService.save(user);UserController bean = applicationContext.getBean(UserController.class);bean.saveAnother();return Result.ok();}@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)public void saveAnother() {User user = new User();user.setPhone("11111801062").setNickName("222");int k = 10/0;userService.save(user);}

在上面的代碼中,因為saveAnother()方法事務行為是Propagation.NESTED,所以它會嵌套在insertUser()方法的事務中執行。其實這個嵌套應該就類似于mysql中的savepoint,比如現在saveAnother()方法中產生了異常,那么就會將事務回滾到當前事務開始的地方。但是!!!!此時saveAnother()方法并不會提交自己的事務!!它的事務會跟insertUser()一起提交!!這也是PROPAGATION_REQUIRES_NEW的本質區別,它是相當于開了一個子事務,跟隨父級事務一起提交,并不會先提交。所以當它發生了異常以后,會先回滾自己的方法到達,將數據恢復到它自己事務開啟之前的狀態。

如果父級對其異常進行了捕獲,那么父級事務可以正常提交。

如果父級事務接到異常不進行捕獲那么自己也會回滾自己的事務。

如果兩個方法都沒問題,那么最終數據會被一起commit。

如果在insertUser()中發生了異常,因為此時saveAnother()方法事務還沒有提交,那么最終就會導致再最后兩個事務都會被回滾。這也就是大家說的A會影響B但是B不會影響A。但是A還是能夠正常接收到B的異常的,處理情況下才不會影響,不處理兩個會一起回滾。

可能對mysql不太熟悉的朋友對這個概念有點陌生。其實就是在一個事務中假如有很多修改的地方,我可以在某一個修改點增加一個savepoint,然后在這個savepoint之后再去執行其余的數據修改。如果修改發生了問題我可以選擇回滾到某一個savepoint,在這個savepoint之前修改的數據并不會被回滾。如果還是不太明白的話可以搜幾篇文章看一下,應該就會很清晰了。

PROPAGATION_SUPPORTS

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

PROPAGATION_NOT_SUPPORTED

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

PROPAGATION_MANDATORY

如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。

PROPAGATION_NEVER

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

二、事務失效的情況

我覺得失效這個事情也很好理解,在排除數據庫不支持的情況。其他情況應該就是你拿到的并不是spring aop生成的代理對象。熟悉spring IOC 的應該都清楚在spring創建Bean時如果檢測到有事務就會提前進行aop生成代理對象。用于執行事務。所以如果說我們事務失效,那么首先判定你當前拿到的是不是代理對象。

舉一個比較常見的例子:

@GetMapping("insert")@Transactional(rollbackFor = Exception.class)public Result insertUser(){User user = new User();user.setPhone("11111801061").setNickName("111");userService.save(user);this.saveAnother();return Result.ok();}@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)public void saveAnother() {User user = new User();user.setPhone("11111801062").setNickName("222");int k = 10/0;userService.save(user);}

?在執行過程中下個斷點很容易就能看到

?可以看到我們拿到的就是當前對象本身,并不是代理對象,所以此時它調用的方法上如果有事務,事務就會失效。

所以此時可以通過ApplicationContext或者注入(解決方案可以自己看一下,有很多種方式)來獲取當前對象的代理類。

?可以看到這樣拿到的就是當前類的代理類了,所以此時再去執行,被調用的方法事務就會生效。

1.數據庫不支持事務。

Spring事務生效的前提是所連接的數據庫要支持事務,如果底層的數據庫都不支持事務,則Spring的事務肯定會失效。例如,如果使用的數據庫為MySQL,并且選用了MyISAM存儲引擎,則Spring的事務就會失效。

2.事務方法未被Spring管理。

?? ?如果事務方法所在的類沒有加載到Spring IOC容器中,也就是說,事務方法所在的類沒有被Spring管理,則Spring事務會失效。

3.方法沒有被public修飾。

?? ?如果事務所在的方法沒有被public修飾,此時Spring的事務會失效。

4.同一類中方法調用。

?? ?如果同一個類中的兩個方法分別為A和B,方法A上沒有添加事務注解,方法B上添加了 @Transactional事務注解,方法A調用方法B,則方法B的事務會失效。

5.未配置事務管理器。

?? ?如果在項目中沒有配置Spring的事務管理器,即使使用了Spring的事務管理功能,Spring的事務也不會生效。

6.方法的事務傳播類型不支持事務。

?? ?如果內部方法的事務傳播類型為不支持事務的傳播類型,則內部方法的事務在Spring中會失效。

7.不正確的捕獲異常。

?? ?不正確的捕獲異常也會導致Spring的事務失效。

8.錯誤的標注異常類型。

?? ?如果在@Transactional注解中標注了錯誤的異常類型,則Spring事務的回滾會失效。

希望對大家能夠有所幫助!如有問題歡迎交流


?

總結

以上是生活随笔為你收集整理的spring 事务传播行为以及失效原因的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。