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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

spring 事务隔离级别和传播行为_Spring事务传播实战

發布時間:2025/3/20 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring 事务隔离级别和传播行为_Spring事务传播实战 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

事務傳播實戰

事務具有四個特性 ——ACID。其中 A 代表原子性,意思是一個事務要么成功(將結果寫入數據庫),要么失敗(不對數據庫有任何影響)。這種方式在一個事務單打獨斗的時候是一個非常好的做法,但是如果在一個批量任務里(假設包含 1000 個獨立的任務),前面的 999 個任務都非常順利、完美、漂亮、酷斃且成功的執行了,等到執行最后一個的時候,結果這個任務非常悲催、很是不幸的失敗了。這時候 Spring 對著前面 999 個成功執行的任務大手一揮說:兄弟們,我們有一個任務失敗了,現在全體恢復原狀!如果這樣的話,那可真是「一頓操作猛如虎,定睛一看原地杵」。

在 Spring 中, 當一個方法調用另外一個方法時,可以讓事務采取不同的策略工作,如新建事務或者掛起當前事務等,這便是事務的傳播行為。Spring 為我們提供了七種傳播行為的策略,通過枚舉類 Propagation 定義,源碼如下:

package org.springframework.transaction.annotation; ? import org.springframework.transaction.TransactionDefinition; ? public enum Propagation { ?/*** 需要事務,它是默認傳播行為,如果當前存在事務,就沿用當前事務,* 去否則新建一個事務運行內部方法*/REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), ?/*** 支持事務,如果當前存在事務,就沿用當前事務,* 如果不存在,則繼續采用無事務的方式運行內部方法*/SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), ?/*** 必須使用事務,如果當前沒有事務,則會拋出異常,* 如果存在當前事務,則沿用當前事務*/MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY), ?/*** 無論當前事務是否存在,都會創建新事務運行方法,* 這樣新事務就可以擁有新的鎖和隔離級別等特性,與當前事務相互獨立*/REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), ?/*** 不支持事務,當前存在事務時,將掛起事務,運行方法*/NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), ?/*** 不支持事務,如果當前方法存在事務,則拋出異常,否則繼續使用無事務機制運行*/NEVER(TransactionDefinition.PROPAGATION_NEVER), ?/** * 在當前方法調用內部方法時,如果內部方法發生異常,* 只回滾內部方法執行過的 SQL ,而不回滾當前方法的事務*/NESTED(TransactionDefinition.PROPAGATION_NESTED); ?...... ? }

本文會研究一些常用場景的事務傳播機制,文中代碼只是突出了關鍵代碼,并不完整,完整代碼請參考文章末尾的鏈接。對照代碼食用更好!

準備兩張表

CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`age` int(255) DEFAULT NULL,PRIMARY KEY (`id`) ) ? CREATE TABLE `course` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`) )

REQUIRED(同生共死)

結論:一旦發生回滾,所有接口都回滾

內層失敗場景

//StudentServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) public class StudentServiceImpl implements StudentService{@Autowiredprivate CourseService courseService;@Resourceprivate StudentMapper studentMapper;@Overridepublic int insert(Student record) {int insert = studentMapper.insert(record);courseService.deleteByPrimaryKey(1);return insert;} } ? // CourseServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) @Service public class CourseServiceImpl implements CourseService{public int deleteByPrimaryKey(Integer id) {int res = 1 / 0; //內層事務失敗return courseMapper.deleteByPrimaryKey(id);} ?//創建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT //給當前事務獲取一個數據庫連接 - Acquired Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] for JDBC transaction//切換連接為手動提交- Switching JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] to manual commit- ==> Preparing: insert into student (`name`, age) values (?, ?) - ==> Parameters: zhangsan(String), null- <== Updates: 1 //加入當前事務 - Participating in existing transaction //加入事務失敗,標記當前事務回滾- Participating transaction failed - marking existing transaction as rollback-only- Setting JDBC transaction [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] rollback-only //回滾 - Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] //釋放連接- Releasing JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] after transaction

外層失敗場景

//StudentServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) public class StudentServiceImpl implements StudentService{@Autowiredprivate CourseService courseService;@Resourceprivate StudentMapper studentMapper;@Overridepublic int insert(Student record) {int insert = studentMapper.insert(record);int res = 1 / 0; //外層事務失敗courseService.deleteByPrimaryKey(1);return insert;} } ? // CourseServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) @Service public class CourseServiceImpl implements CourseService{public int deleteByPrimaryKey(Integer id) {return courseMapper.deleteByPrimaryKey(id);} ? ?

日志如下

//創建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT HikariPool-1 - Starting... HikariPool-1 - Start completed. - Acquired Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] for JDBC transaction - Switching JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] to manual commit //回滾 - Initiating transaction rollback - Rolling back JDBC transaction on Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] - Releasing JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] after transaction

REQUIRES_NEW(不受外層影響)

當內部方法的傳播行為設置為 REQUIRES_NEW 時,內部方法會先將外部方法的事務掛起,然后開啟一個新的事務,等內部方法執行完后再提交外層事務。

結論:

  • 內部回滾會導致外部事務也回滾
  • 外層回滾不影響內層的提交
  • 內層失敗場景

    @Transactional(propagation = Propagation.REQUIRES_NEW) @Service public class CourseServiceImpl implements CourseService{ ?public int deleteByPrimaryKey(Integer id) {int res = 1 / 0;return courseMapper.deleteByPrimaryKey(id);}

    日志輸出如下

    //創建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULTHikariPool-1 - Starting...HikariPool-1 - Start completed.- Acquired Connection [HikariProxyConnection@1700751834 wrapping com.mysql.cj.jdbc.ConnectionImpl@43b5021c] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@1700751834 wrapping com.mysql.cj.jdbc.ConnectionImpl@43b5021c] to manual commit//掛起當前事務,創建新事務- Suspending current transaction, creating new transaction with name [com.cxf.data.service.impl.CourseServiceImpl.deleteByPrimaryKey]- Acquired Connection [HikariProxyConnection@1888400144 wrapping com.mysql.cj.jdbc.ConnectionImpl@6ebc9573] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@1888400144 wrapping com.mysql.cj.jdbc.ConnectionImpl@6ebc9573] to manual commit//回滾- Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@1888400144 wrapping com.mysql.cj.jdbc.ConnectionImpl@6ebc9573]- Releasing JDBC Connection [HikariProxyConnection@1888400144 wrapping com.mysql.cj.jdbc.ConnectionImpl@6ebc9573] after transaction //內部事務回滾完成后重新喚醒掛起的事務 - Resuming suspended transaction after completion of inner transaction //執行回滾- Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@1700751834 wrapping com.mysql.cj.jdbc.ConnectionImpl@43b5021c]- Releasing JDBC Connection [HikariProxyConnection@1700751834 wrapping com.mysql.cj.jdbc.ConnectionImpl@43b5021c] after transaction

    外層失敗場景

    外層失敗不影響內層事務的成功提交

    //StudentServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) public class StudentServiceImpl implements StudentService{@Autowiredprivate CourseService courseService;@Resourceprivate StudentMapper studentMapper;@Overridepublic int insert(Student record) {int insert = studentMapper.insert(record);courseService.deleteByPrimaryKey(1);int res = 1 / 0; //外層事務失敗return insert;} } ? // CourseServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) @Service public class CourseServiceImpl implements CourseService{public int deleteByPrimaryKey(Integer id) {return courseMapper.deleteByPrimaryKey(id);}

    日志如下

    //創建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT- Acquired Connection [HikariProxyConnection@674667952 wrapping com.mysql.cj.jdbc.ConnectionImpl@30893e08] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@674667952 wrapping com.mysql.cj.jdbc.ConnectionImpl@30893e08] to manual commit- ==> Preparing: insert into student (`name`, age) values (?, ?) - ==> Parameters: zhangsan(String), null- <== Updates: 1//掛起當前事務,并創建一個新事務- Suspending current transaction, creating new transaction with name [com.cxf.data.service.impl.CourseServiceImpl.deleteByPrimaryKey]- Acquired Connection [HikariProxyConnection@1857852787 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e977098] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@1857852787 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e977098] to manual commit Preparing: delete from course where id = ? arameters: 1(Integer)Updates: 1//提交內層事務--- 注意,內層成功了,通過查看數據庫發現記錄被成功刪除- Initiating transaction commit- Committing JDBC transaction on Connection [HikariProxyConnection@1857852787 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e977098]- Releasing JDBC Connection [HikariProxyConnection@1857852787 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e977098] after transaction- Resuming suspended transaction after completion of inner transaction//回滾外層事務 - Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@674667952 wrapping com.mysql.cj.jdbc.ConnectionImpl@30893e08]- Releasing JDBC Connection [HikariProxyConnection@674667952 wrapping com.mysql.cj.jdbc.ConnectionImpl@30893e08] after transaction

    NESTED(不受內層影響)

    當內部方法的傳播行為設置為 NESTED 時,內部方法會開啟一個新的嵌套事務

    每個 NESTED 事務執行前會將當前操作保存下來,叫做 savepoint (保存點),如果當前 NESTED 事務執行失敗,則回滾到之前的保存點,以便之前的執行結果不受當前 NESTED 事務的影響,從而內層方法回滾,則并不影響外層方法的提交。

    NESTED 事務在外部事務提交以后自己才會提交。

    外層為REQUIRED

    內層失敗場景

    結論: 內層回滾不影響外層的執行

    //StudentServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) public class StudentServiceImpl implements StudentService{ ?public int insert(Student record) {int insert = studentMapper.insert(record);courseService.deleteByPrimaryKey(1);return insert;} //CourseServiceImpl.java @Transactional(propagation = Propagation.NESTED)//注意事務傳播機制的修改 @Service public class CourseServiceImpl implements CourseService{ ?@Overridepublic int deleteByPrimaryKey(Integer id) {int res = 1 / 0; //內層失敗return courseMapper.deleteByPrimaryKey(id);}

    日志如下

    //創建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULTHikariPool-1 - Starting...HikariPool-1 - Start completed.- Acquired Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] to manual commit- ==> Preparing: insert into student (`name`, age) values (?, ?) - ==> Parameters: zhangsan(String), null- <== Updates: 1//創建嵌套事務, 掛起外層事務- Creating nested transaction with name [com.cxf.data.service.impl.CourseServiceImpl.deleteByPrimaryKey]//嵌套事務回滾到savepoint- Rolling back transaction to savepoint- Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] //繼續完成外層事務- Releasing JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] after transaction

    外層失敗場景

    外層失敗會導致內層回滾

    //StudentServiceImpl.java @Transactional(propagation = Propagation.REQUIRED) public class StudentServiceImpl implements StudentService{ ?public int insert(Student record) {int insert = studentMapper.insert(record);courseService.deleteByPrimaryKey(1);int res = 1 / 0; //外層失敗return insert;} //CourseServiceImpl.java @Transactional(propagation = Propagation.NESTED)//注意事務傳播機制的修改 @Service public class CourseServiceImpl implements CourseService{ ?@Overridepublic int deleteByPrimaryKey(Integer id) {return courseMapper.deleteByPrimaryKey(id);}

    日志如下

    //新建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULTHikariPool-1 - Starting...HikariPool-1 - Start completed.- Acquired Connection [HikariProxyConnection@247334525 wrapping com.mysql.cj.jdbc.ConnectionImpl@3a4ab7f7] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@247334525 wrapping com.mysql.cj.jdbc.ConnectionImpl@3a4ab7f7] to manual commit- ==> Preparing: insert into student (`name`, age) values (?, ?) - ==> Parameters: zhangsan(String), null- <== Updates: 1//創建內嵌事務- Creating nested transaction with name [com.cxf.data.service.impl.CourseServiceImpl.deleteByPrimaryKey] Preparing: delete from course where id = ? arameters: 1(Integer)Updates: 1//釋放savepoint - Releasing transaction savepoint//內嵌事務回滾- Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@247334525 wrapping com.mysql.cj.jdbc.ConnectionImpl@3a4ab7f7]- Releasing JDBC Connection [HikariProxyConnection@247334525 wrapping com.mysql.cj.jdbc.ConnectionImpl@3a4ab7f7] after transaction

    查看數據庫發現外層失敗會導致內層回滾。

    MANDATORY

    必須在一個已有的事務中執行,否則報錯

    如果外層NOT_SUPPORTED,而內層是MANDATORY,則會拋異常

    Should roll back transaction but cannot - no transaction available org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

    NEVEL

    必須在一個沒有的事務中執行,否則報錯

    SUPPORTS

    如果其他bean調用這個方法時,其他bean聲明了事務,則就用這個事務,如果沒有聲明事務,那就不用事務

    外層使用REQUIRED,內層使用SUPPORTS

    //創建事務 - Creating new transaction with name [com.cxf.data.service.impl.StudentServiceImpl.insert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT- Acquired Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] for JDBC transaction- Switching JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] to manual commit- ==> Preparing: insert into student (`name`, age) values (?, ?) - ==> Parameters: zhangsan(String), null- <== Updates: 1 //加入已有事務 - Participating in existing transaction- Participating transaction failed - marking existing transaction as rollback-only- Setting JDBC transaction [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] rollback-only- Initiating transaction rollback- Rolling back JDBC transaction on Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d]- Releasing JDBC Connection [HikariProxyConnection@2096598149 wrapping com.mysql.cj.jdbc.ConnectionImpl@ebe067d] after transaction

    總結

    區別

    REQUIRES_NEW 最為簡單,不管當前有無事務,它都會開啟一個全新事務,既不影響外部事務,也不會影響其他內部事務,真正的井水不犯河水,堅定而獨立。

    REQUIRED 在沒有外部事務的情況下,會開啟一個事務,不影響其他內部事務;而當存在外部事務的情況下,則會與外部事務還有其他內部事務同命運共生死。有條件會直接上,沒條件是會自己創造條件,然后再上。

    NESTED 在沒有外部事務的情況下與 REQUIRED 效果相同;而當存在外部事務的情況下,則與外部事務生死與共,但與其他內部事務互不相干。要么孑然一身,要么誓死追隨主公(外部事務)。

    傳播

  • REQUIRED(同生共死)
  • 當兩個方法的傳播機制都是REQUIRED時,如果一旦發生回滾,兩個方法都會回滾

  • REQUIRES_NEW(內層可獨立提交)
  • 當內層方法傳播機制為REQUIRES_NEW,會開啟一個新的事務,并單獨提交方法,所以外層方法的回滾并不影響內層方法事務提交

  • NESTED(外層可單獨提交)
    當外層方法為REQUIRED,內層方法為NESTED時,內層方法開啟一個嵌套事務;
    當外層方法回滾時,內層方法也會回滾;反之,如果內層方法回滾,則并不影響外層方法的提交
  • 關注"大數據那點事兒",回復‘事務傳播’獲取完整源碼

    總結

    以上是生活随笔為你收集整理的spring 事务隔离级别和传播行为_Spring事务传播实战的全部內容,希望文章能夠幫你解決所遇到的問題。

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