Spring 中的事務

🧾 一、什么是事務?

🧠 通俗理解:

事務 = 一組操作,要么全部成功,要么全部失敗,不能只做一半。

比如你轉賬:

  • A 賬戶扣錢
  • B 賬戶加錢

如果 A 扣了錢但 B 沒收到,那就出問題了!這就是 數據不一致,事務就是為了解決這類問題。

? 二、事務的四大特性(ACID)

特性解釋
原子性(Atomicity)要么全部成功,要么全部失敗
一致性(Consistency)操作前后數據處于一致狀態
隔離性(Isolation)多個事務互不干擾
持久性(Durability)提交后的數據是永久保存的

💡 三、Spring 中的事務管理方式

Spring 支持兩種事務管理方式:

1. 編程式事務管理(不常用)

手動寫事務邊界,代碼控制事務提交/回滾。

TransactionStatus status = txManager.getTransaction(...);
try {// 業務代碼txManager.commit(status);
} catch (Exception e) {txManager.rollback(status);
}

🚫 缺點:代碼侵入性強、重復、易出錯。實際開發中不推薦。

2. 聲明式事務管理 ?(主流)

通過注解或配置來管理事務,干凈、簡潔、優雅!

@Service
public class AccountService {@Transactionalpublic void transfer() {// 扣錢// 加錢// 如果中間出異常,自動回滾}
}

📌 四、@Transactional 注解詳解

@Transactional 是 Spring 中聲明事務的注解,作用范圍可以是類或方法。

常用屬性:

屬性含義
propagation事務傳播行為(默認 REQUIRED
isolation事務隔離級別
rollbackFor指定哪些異常觸發回滾(默認是運行時異常)
readOnly是否只讀事務
timeout設置事務超時時間

🔄 五、事務的傳播行為(重點)

行為含義
REQUIRED(默認)有事務就加入,沒有就新建
REQUIRES_NEW每次都新建一個事務,原事務掛起
NESTED嵌套事務,有獨立的回滾點
SUPPORTS有事務就用,沒有就不用
NOT_SUPPORTED不支持事務,掛起當前事務
NEVER當前不能存在事務,否則拋異常
MANDATORY必須存在事務,否則拋異常

🔒 六、事務隔離級別(對應數據庫的)

隔離級別解決的問題說明
DEFAULT使用數據庫默認
READ_UNCOMMITTED臟讀最低,性能高但不安全
READ_COMMITTED不可重復讀常用,如 Oracle 默認
REPEATABLE_READ幻讀MySQL 默認
SERIALIZABLE最強隔離安全但性能差

💣 七、事務生效的注意事項(易踩坑)

🚨 1. 事務方法必須是 public

@Transactional // 正確
public void doSomething() {}

🚨 2. 方法 不能是同一個類中內部調用

// 錯誤示例:事務不起作用
public void outer() {this.inner(); // 沒經過代理
}

? 正確做法:通過代理調用

  • 注入自身(@Lazy 方式)
  • 拆到 Service 層讓 Spring 來代理調用

🚨 3. 默認只對 運行時異常 回滾

@Transactional(rollbackFor = Exception.class)

🧪 八、事務管理器(Spring 內部機制)

Spring 通過 PlatformTransactionManager 接口進行統一管理。

常用實現類:

實現類場景
DataSourceTransactionManager使用 JDBC、MyBatis
JpaTransactionManager使用 JPA/Hibernate
ChainedTransactionManager多數據源事務

? 九、一句話總結

Spring 的事務機制讓你只關心業務邏輯,不用手動管理事務,聲明式的方式簡潔高效,是真正的企業級利器。

@Transactional 實現原理

它的實現原理非常巧妙,實際上是通過 AOP(面向切面編程)代理機制 實現的。這一機制讓你無需寫一行事務管理的代碼,Spring 會自動為你處理。

我們一起來詳細剖析一下底層原理!🧠

💡 一、核心原理概覽

1. AOP + 代理

@Transactional 注解是基于 AOP(面向切面編程)實現的,具體來說,Spring 會為標記了 @Transactional 注解的方法創建一個代理對象,這個代理對象會在方法執行前后進行事務的開啟、提交和回滾等處理。

2. 動態代理

Spring 通過動態代理技術(JDK 代理或 CGLIB 代理)生成一個代理對象,這個對象會攔截你的方法調用,在方法調用前后進行事務管理的相關操作。

3. 事務管理器

Spring 會利用配置的 事務管理器(PlatformTransactionManager 來處理事務的開啟、提交、回滾等操作。

🛠? 二、@Transactional 的工作流程

步驟 1:創建代理對象

  • 代理方式:Spring 使用 JDK 動態代理CGLIB 代理 來生成代理對象。默認情況下,如果目標類實現了接口,Spring 會使用 JDK 動態代理;如果沒有實現接口,則使用 CGLIB 創建子類代理。

步驟 2:事務管理器的配置

  • @Transactional 依賴于 事務管理器 來執行事務操作,Spring 根據你配置的 PlatformTransactionManager 來管理事務。常見的事務管理器有:
    • DataSourceTransactionManager:用于 JDBC。
    • JpaTransactionManager:用于 JPA(如 Hibernate)。
    • HibernateTransactionManager:用于 Hibernate。

步驟 3:方法執行前,開啟事務

當你調用被 @Transactional 注解標記的方法時,Spring 代理會攔截這個方法的調用,首先會判斷當前方法是否需要開啟新事務(即,方法的傳播行為是 REQUIRED)。如果需要開啟新事務,則會通過 事務管理器 啟動一個新的事務。

  • 事務的傳播行為(propagation:比如,REQUIRED 表示如果當前沒有事務,就新建一個事務,如果已有事務,就加入到當前事務中。

步驟 4:方法執行中,進行事務操作

方法執行過程中,Spring 會維持該事務的狀態,直到方法執行完畢。

  • 事務隔離級別(isolation:隔離級別決定了事務之間如何互相影響(比如,是否允許臟讀、不可重復讀等)。

步驟 5:方法執行后,提交或回滾事務

  • 方法正常完成:如果方法執行沒有異常,Spring 會在方法結束后提交事務。
  • 方法出現異常:如果方法拋出異常,Spring 會根據 @Transactional 配置的異常回滾規則,判斷是否回滾該事務。

?? 三、事務管理的關鍵組件

1. TransactionInterceptor:核心事務攔截器

  • Spring 使用 TransactionInterceptor 來處理所有帶有 @Transactional 注解的方法,它是事務管理的關鍵攔截器。
  • 該攔截器負責從容器中獲取事務管理器、根據事務配置設置事務屬性(如隔離級別、傳播行為等),并且控制事務的開啟、提交和回滾。

2. TransactionProxyFactoryBean:生成代理對象

  • TransactionProxyFactoryBean 是 Spring 用來創建事務代理的工廠,實際上它會根據注解或配置生成一個代理對象。
  • 這個代理對象會攔截對事務方法的調用,并在調用方法前后進行事務的處理。

3. PlatformTransactionManager:事務管理器

  • 事務管理器(如 DataSourceTransactionManager)負責實際的事務操作,包括開啟事務、提交事務、回滾事務等。

🔍 四、事務處理的實際步驟

  1. 方法調用被代理對象攔截
    • @Transactional 標記的方法會被 AOP 代理攔截,調用 TransactionInterceptor
  2. 事務攔截器執行邏輯
    • TransactionInterceptor 會根據 @Transactional 配置,從容器中獲取 事務管理器PlatformTransactionManager)。
  3. 判斷事務傳播行為
    • 如果當前沒有事務(例如沒有正在進行的事務),根據傳播行為決定是否新建事務。默認是 REQUIRED,即如果沒有事務,創建一個新的事務。
  4. 開啟事務
    • 通過 PlatformTransactionManager 開啟一個事務(實際是對底層數據庫操作的封裝)。
  5. 執行目標方法
    • 目標方法執行,在方法內的操作都在同一個事務中進行。
  6. 提交或回滾事務
    • 如果方法執行沒有拋出異常,Spring 提交事務;如果拋出指定異常(如運行時異常),Spring 會回滾事務。

💡 五、示例:@Transactional 事務的底層工作

假設你有一個服務類 AccountService,方法 transfer()@Transactional 注解標記:

@Service
public class AccountService {@Transactionalpublic void transfer() {// 扣款操作// 加款操作// 如果中間出錯,Spring 會自動回滾}
}
  • Spring 會生成一個代理對象 AccountService,攔截 transfer() 方法調用。
  • 代理對象通過 TransactionInterceptor 來控制事務的開啟、提交或回滾。
  • 在調用 transfer() 方法之前,Spring 會檢查是否需要開啟事務,創建事務并將其綁定到當前線程(一般是通過 ThreadLocal 來綁定)。
  • transfer() 執行完后,如果沒有異常,Spring 會提交事務;如果出現異常,Spring 會根據配置回滾事務。

🧠 六、總結

@Transactional 的底層原理依賴于 AOP + 動態代理,通過 事務攔截器TransactionInterceptor)和 事務管理器PlatformTransactionManager)來實現事務的控制。Spring 提供了靈活的事務傳播行為和隔離級別,讓事務控制變得更加簡單和清晰。

Spring 事務在什么情況下會失效?

1. 方法是 privateprotected

Spring 的事務管理是基于 AOP(面向切面編程) 實現的,而 AOP 需要代理對象來執行方法。如果方法是 privateprotected,Spring 的 AOP 代理是無法攔截到該方法的,因此事務無法生效。

解決方法:

  • 將事務方法設置為 public,這樣 Spring 的代理對象才能正確地攔截并應用事務。
@Transactional
public void transfer() { // 必須是 public// 業務邏輯
}

2. 在同一類內調用事務方法

當一個類內部的事務方法相互調用時,事務是不會生效的。這是因為 事務代理 是基于代理對象的,而類內部方法調用是直接調用對象的方法,不會經過代理。

例子:

@Service
public class AccountService {@Transactionalpublic void transfer() {// 扣款操作this.otherMethod(); // 調用同一個類中的方法}@Transactionalpublic void otherMethod() {// 加款操作}
}

在這個例子中,transfer() 調用了 otherMethod(),但 @Transactional 注解沒有生效,因為調用 this.otherMethod() 是直接調用類內部的方法,不會經過代理。

解決方法:

  • 拆分方法,將事務性方法提取到另一個服務類中,確保 Spring 代理對象能夠接管方法調用。

3. 沒有配置事務管理器

Spring 中的事務是依賴于 PlatformTransactionManager 來進行管理的。如果沒有配置或注入正確的事務管理器,事務就無法生效。

解決方法:

  • 確保在 applicationContext.xmlSpring Boot 配置類中配置了正確的事務管理器。

Spring Boot 示例:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {return new JpaTransactionManager(entityManagerFactory);}
}

4. 事務方法拋出了非 RuntimeException 異常

Spring 默認只對 運行時異常 (RuntimeException 和其子類) 回滾事務。如果你在方法中拋出了一個 檢查型異常(checked exception(如 IOExceptionSQLException 等),默認情況下,Spring 不會回滾事務。

解決方法:

  • 使用 @TransactionalrollbackFor 屬性來指定哪些異常需要回滾事務。
@Transactional(rollbackFor = Exception.class)  // 指定回滾異常
public void transfer() throws IOException {// 業務邏輯
}

5. 事務被 @Transactional 注解的代理方法外部調用

Spring 的事務管理依賴于 代理對象,如果事務方法被其他 非 Spring 管理的對象調用,Spring 的事務也不會生效。

解決方法:

  • 確保事務方法僅通過 Spring 管理的代理對象來調用。避免手動調用 new 創建的對象或外部非 Spring 管理的對象。

6. 事務的傳播行為不正確

事務的傳播行為(propagation)控制了事務的嵌套和傳播方式。如果設置不當,事務可能不會按預期工作。

例如,如果你使用了 REQUIRES_NEW 傳播行為,每次方法都會啟動一個新的事務,原事務會被掛起。如果這時原事務出現問題,它就無法回滾。

解決方法:

  • 確保事務的傳播行為與業務場景相匹配。常見的設置是 REQUIRED(默認),它會加入當前事務,如果沒有事務,則創建一個新的事務。
@Transactional(propagation = Propagation.REQUIRED)
public void transfer() {// 事務邏輯
}

7. 非事務方法調用事務方法時,事務失效

如果一個非事務方法調用了帶有 @Transactional 的方法,在一些情況下事務不會生效。特別是在 非 Spring 管理的對象 上,事務不會生效。

解決方法:

  • 確保事務方法是由 Spring 管理的代理對象來調用,避免在非 Spring 管理的對象中調用事務方法。

8. 只讀事務修改數據

@Transactional 中的 readOnly 屬性告訴 Spring 該事務是只讀的。如果在只讀事務中執行了寫操作(比如更新數據庫),在某些數據庫系統中事務可能不會按預期提交,甚至會拋出異常。

解決方法:

  • 確保只讀事務只用于查詢操作,避免在只讀事務中執行寫操作。
@Transactional(readOnly = true)
public List<User> getAllUsers() {return userRepository.findAll();
}

🚦 九、總結

Spring 事務失效的常見原因:

  1. 事務方法是 privateprotected
  2. 方法在同一類中被調用(直接調用,未通過代理)。
  3. 沒有配置事務管理器
  4. 拋出了非 RuntimeException 異常
  5. 事務方法被非 Spring 管理的對象外部調用
  6. 事務的傳播行為設置不當
  7. 非事務方法調用事務方法時,事務失效
  8. 只讀事務進行數據修改

避免失效的建議:

  • 確保事務方法是 public
  • 使用 Spring 管理的代理對象來調用事務方法。
  • 正確配置事務管理器。
  • 根據需求調整 rollbackForpropagation 配置。
  • 使用 readOnly 標記僅進行查詢的事務。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/75835.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75835.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/75835.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flutter極速接入IM聊天功能并支持鴻蒙

Flutter極速接入IM聊天功能并支持鴻蒙 如果你們也是Flutter項目&#xff0c;想快速接入聊天&#xff0c;包括聊天的UI界面&#xff0c;強烈推薦這一家。因為我們已經完成了集成&#xff0c;使用非常穩定&#xff0c;集成也非常快捷方便。 而且&#xff0c;就在今天&#xff0c…

C# 類庫生成后自動復制到指定目錄

C# 類庫生成后自動復制到指定目錄 在C#中,當你開發了一個類庫項目(通常是.NET Core或.NET Framework項目),你可能會希望在構建(Build)完成后自動將生成的DLL文件復制到指定的目錄。有幾種方法可以實現這個需求,下面是一些常用的方法: 方法1:使用MSBuild的AfterBuild…

13-產品經理-產品多分支平臺管理

禪道16.0版本開始&#xff0c;優化和增強了產品的分支/平臺功能&#xff0c;主要特點如下&#xff1a; 多分支/平臺功能兼容各種大小型項目&#xff0c;項目/迭代可以關聯對應產品的某個分支/平臺。分支/平臺支持靈活管理&#xff0c;可以把分支/平臺理解為時間層面的概念&…

手搓多模態-04 歸一化介紹

在機器學習中&#xff0c;歸一化是一個非常重要的工具&#xff0c;它能幫助我們加速訓練的速度。在我們前面的SiglipVisionTransformer 中&#xff0c;也有用到歸一化層&#xff0c;如下代碼所示&#xff1a; class SiglipVisionTransformer(nn.Module): ##視覺模型的第二層&am…

Qt 入門 1 之第一個程序 Hello World

Qt 入門1之第一個程序 Hello World 直接上操作步驟從頭開始認識&#xff0c;打開Qt Creator&#xff0c;創建一個新項目&#xff0c;并依次執行以下操作 在Qt Creator中&#xff0c;一個Kits 表示一個完整的構建環境&#xff0c;包括編譯器、Qt版本、調試器等。在上圖中可以直…

深入理解MySQL:核心特性、優化與實踐指南

MySQL是一個開源的關系型數據庫管理系統(RDBMS)&#xff0c;由瑞典MySQL AB公司開發&#xff0c;目前屬于Oracle公司。它是目前世界上最流行的開源數據庫之一&#xff0c;廣泛應用于各種規模的Web應用和企業系統中。 目錄 一、核心特點 關系型數據庫&#xff1a; 開源免費&am…

Linux 系統安裝與優化全攻略:打造高效開發環境

一、開篇引言 &#xff08;一&#xff09;Linux 系統的廣泛應用 Linux 憑借其開源、穩定且安全的特性&#xff0c;在服務器、嵌入式設備以及開發環境等領域都有著極為廣泛的應用。 &#xff08;二&#xff09;撰寫本文的目的 為讀者提供一套全面且實用的指南&#xff0c;助…

代碼訓練day22回溯算法p1

1.組合 &#xff08;1&#xff09;模板 void backtracking(參數) {if (終止條件) {存放結果;return;}for (選擇&#xff1a;本層集合中元素&#xff08;樹中節點孩子的數量就是集合的大小&#xff09;) {處理節點;backtracking(路徑&#xff0c;選擇列表); // 遞歸回溯&#…

2024華為OD機試真題-任務最優調度(C++/Java/Python)-E卷-200分

2024華為OD機試最新E卷題庫-(D卷+E卷)-(JAVA、Python、C++) 目錄 題目描述 輸入描述 輸出描述 用例1 考點 題目解析 代碼 c++ java python 題目描述 給定一個正整數數組表示待系統執行的任務列表,數組的每一個元素代表一個任務,元素的值表示該任務的類型。請計算執…

每日習題:20250407

2025 2025 2025年 04 04 04月 06 06 06日 題目 1 設 X X X是實隨機變量&#xff0c;任意光滑的函數 f : R → R f:\mathbf{R} \rightarrow \mathbf{R} f:R→R&#xff0c;都有&#xff1a; E ( X f ( X ) ) E ( f ′ ( X ) ) E\left(Xf(X)\right)E\left(f(X)\right) E(Xf(X)…

TensorRT 有什么特殊之處

一、TensorRT的定義與核心功能 TensorRT是NVIDIA推出的高性能深度學習推理優化器和運行時庫&#xff0c;專注于將訓練好的模型在GPU上實現低延遲、高吞吐量的部署。其主要功能包括&#xff1a; 模型優化&#xff1a;通過算子融合&#xff08;合并網絡層&#xff09;、消除冗余…

JCR一區文章,壯麗細尾鷯鶯算法Superb Fairy-wren Optimization-附Matlab免費代碼

本文提出了一種新穎的基于群體智能的元啟發式優化算法——壯麗細尾鷯優化算法&#xff08;SFOA&#xff09;,SFOA從精湛的神仙鶯的生活習性中汲取靈感。融合了精湛的神仙鶯群體中幼鳥的發育、繁殖后鳥類喂養幼鳥的行為以及它們躲避捕食者的策略。通過模擬幼鳥生長、繁殖和攝食階…

使用Ubuntu18恢復群暉nas硬盤數據外接usb

使用Ubuntu18恢復群暉nas硬盤數據外接usb 1. 接入硬盤2.使用Ubuntu183.查看nas硬盤信息3. 掛載nas3.1 掛載損壞nas硬盤(USB)3.2 掛載當前運行的nas 4. 拷貝數據分批傳輸 5. 新舊數據對比 Synology NAS 出現故障&#xff0c;DS DiskStation損壞&#xff0c;則可以使用計算機和 U…

linux 安裝 mysql記錄

sudo apt-get install mysql-server 一直報錯&#xff0c;按照下面的終于安裝出來了 這個鏈接 https://cn.linux-console.net/?p13784 第 1 步&#xff1a;要刪除 MySQL 及其所有依賴項&#xff0c;請執行以下命令&#xff1a; sudo apt-get remove --purge mysql* 第 2 步…

UE5學習筆記 FPS游戲制作35 使用.csv配置文件

文章目錄 導入.csv要求首先創建一個結構體導入配置文件讀取配置 導入 .csv要求 第一行必須包含標題 第一列的內容必須不能重復&#xff0c;因為第一列會被當成行的名字&#xff0c;在數據處理中發揮類似于字典的key的作用 當前的配置文件內容如下 首先創建一個結構體 結構…

談談策略模式,策略模式的適用場景是什么?

一、什么是策略模式&#xff1f;?? 策略模式&#xff08;Strategy Pattern&#xff09;屬于??行為型設計模式??。核心思路是將一組??可替換的算法??封裝在獨立的類中&#xff0c;使它們可以在運行時動態切換&#xff0c;同時使客戶端代碼與具體算法解耦。它包含三個…

AGI大模型(10):prompt逆向-巧借prompt

1 提示詞逆向 明確逆向提示詞?程概念 我們可以給ChatGPT提供?個簡潔的提示詞,讓它能夠更準確地理解我們所討論的“逆向提示詞?程”是什么意思,并通過這個思考過程,幫它將相關知識集中起來,進?構建?個專業的知識領域 提示詞:請你舉?個簡單的例?,解釋?下逆向pro…

IntelliJ IDEA全棧Git指南:從零構建到高效協作開發

文章目錄 摘要 正文一、環境搭建&#xff1a;5分鐘完成Git與IDEA的深度綁定二、基礎操作&#xff1a;IDEA如何將Git效率提升300%三、分支管理&#xff1a;高并發團隊的協作秘籍四、強制覆蓋&#xff1a;高風險操作的生存指南五、實戰避坑&#xff1a;血淚經驗總結六、基于老項目…

指針的補充(用于學習筆記的記錄)

1.指針基礎知識 1.1 指針變量的定義和使用 指針也是一種數據類型&#xff0c;指針變量也是一種變量 指針變量指向誰&#xff0c;就把誰的地址賦值給指針變量 #include<stdio.h>int main() {int a 0;char b 100;printf("%p,%p \n", &a,&b); // …

【Cursor/VsCode】在文件列表中不顯示.meta文件

打開設置文件 教程&#xff1a;【Cursor】打開Vscode設置 在settings.json里配置這個&#xff1a; "files.exclude": {"**/*.meta": true }