Spring事務原理 二

在上一篇博文《Spring事務原理 一》中,我們熟悉了Spring聲明式事務的AOP原理,以及事務執行的大體流程。

本文中,介紹了Spring事務的核心組件、傳播行為的源碼實現。下一篇中,我們將結合案例,來講解實戰中有關事務的易錯點。

本文中源碼來自Spring 5.3.x分支,github源碼地址:GitHub - spring-projects/spring-framework: Spring Framework

一 Spring事務的核心組件

了解相關類和接口,看看Spring對概念、術語是如何封裝的?

1.1 PlatformTransactionManager

事務管理器接口,負責獲取數據庫連接,事務的開啟、提交和回滾。

有抽象實現AbstractPlatformTransactionManager,其中定義了doGetTransaction、doBegin、doCommit、doRollback等抽象方法,待子類實現。

AbstractPlatformTransactionManager中有幾個值得關注的屬性:

// 是否允許嵌套事務
private boolean nestedTransactionAllowed = false;// 局部失敗時全局回滾,當為false時,部分失敗則不回滾
private boolean globalRollbackOnParticipationFailure = true;private boolean failEarlyOnGlobalRollbackOnly = false;private boolean rollbackOnCommitFailure = false;

它有以下常見子類:

  • DataSourceTransactionManager:用于JDBC和MyBatis等基于數據源的事務管理。
  • HibernateTransactionManager:用于Hibernate框架的事務管理。
  • JpaTransactionManager:用于JPA(Java Persistence API)的事務管理。

DataSourceTransactionManager

該類中定義了兩個屬性,對doCommit等抽象方法提供實現。

  • doGetTransaction方法:創建一個DataSourceTransactionObject對象,設置connection。

  • doBegin方法:執行事務前的準備工作,如設置
    • 如果DataSourceTransactionObject沒有連接,則獲取一個連接
    • 根據TransactionDefinition,為connection設置屬性,如isolationLevel、readOnly、timeout;
    • connection.setAutoCommit(false),關閉自動提交;
    • 將connection與當前線程綁定;
  • doCommit方法:從TransactionStatus中獲取TransactionObject,拿到connection調用commit();

  • doRollback方法:與doCommit實現相似,只是調connection.rollback();

1.2 TransactionDefinition

定義事務的屬性,如隔離級別、傳播行為、超時時間等。

隔離級別(Isolation Level):定義了事務之間的隔離程度,常見的有:

  • DEFAULT:使用數據庫默認的隔離級別。
  • READ_UNCOMMITTED:允許讀取未提交的數據,可能導致臟讀。
  • READ_COMMITTED:只能讀取已提交的數據,避免臟讀。
  • REPEATABLE_READ:確保在同一事務中多次讀取同一數據時,結果一致。
  • SERIALIZABLE:最高的隔離級別,確保事務串行執行,避免臟讀、不可重復讀和幻讀。

超時時間(Timeout):事務的超時時間,超過該時間未完成則自動回滾。

只讀(Read-only):指定事務是否為只讀事務,優化性能。

在子類DefaultTransactionDefinition中,可以看到默認值:PROPAGATION_REQUIRED、ISOLATION_DEFAULT、TIMEOUT_DEFAULT、非readOnly。

當使用@Transactional時,會將注解屬性解析成一個TransactionDefinition對象。

1.3 TransactionStatus

表示事務的狀態,提供了以下方法:

  • isNewTransaction():判斷當前事務是否為新事務。
  • hasSavepoint():判斷是否存在保存點(用于嵌套事務)。
  • setRollbackOnly():標記事務為回滾狀態。
  • isRollbackOnly():判斷事務是否被標記為回滾。

在子類DefaultTransactionStatus中,有這些屬性

private boolean rollbackOnly = false;private boolean completed = false;private final Object transaction;private final boolean newTransaction;private final boolean newSynchronization;private final boolean readOnly;

1.4 TransactionSynchronizationManager

事務同步管理器,用于將事務相關信息與當前線程綁定,以支持各種事務傳播行為。其中有多個ThreadLocal屬性。

為什么保存連接的resources是Map類型?因為支持多數據源,當一個方法中操作多個數據庫時,線程中就得保存多個connectionHolder對象,因此使用Map結構,key就是dataSource對象。

1.5 TransactionInterceptor

事務攔截器,就是AOP的代理邏輯,具體實現在TransactionAspectSupport#invokeWithinTransaction中。

大體流程為:

  1. 獲取當前方法的@Transaction注解屬性,創建TransactionDefinition對象;
  2. 獲取TransactionManager對象;
  3. 根據方法名生成事務名;
  4. 如有必要則創建事務,并處理傳播行為;
  5. 在try中執行下一個interceptor或被代理對象中方法;
  6. 異常時先回滾事務,正常時提交事務;
  7. 當前方法執行結束,還原TransactionInfo(恢復上層方法的事務信息)。

二 事務的傳播機制

2.1 什么是事務傳播

在日常開發中,業務代碼中經常出現方法間調用,比如購物時下單減和庫存:

import com.xiakexing.dao.InventoryDao;
import com.xiakexing.dao.OrderDao;
import com.xiakexing.entity.Order;public class OrderService {private OrderDao orderDao;private InventoryDao inventoryDao;public void saveOrder(Order order) {orderDao.save(order);updateInventory(order.getCode(), order.getCount());}public void updateInventory(String code, int count) {inventoryDao.update(code, count);}
}
  1. saveOrder()和updateInventory(),所有sql需要在同一個事務中;
  2. 單獨調用updateInventory()時,如進貨時不需要事務。

可見,updateInventory方法,在不同場景下對事務有不同要求。Spring中又如何實現呢?

Spring定義了傳播行為(Propagation Behavior),定義了方法間調用時事務如何傳遞,類型有:

  • REQUIRED:如果當前線程存在事務,則加入該事務;如果不存在,則創建一個新事務。
  • REQUIRES_NEW:總是創建一個新事務,如果當前線程存在事務,則掛起當前事務。
  • SUPPORTS:如果當前線程存在事務,則加入該事務;如果不存在,則以非事務方式執行。
  • NOT_SUPPORTED:以非事務方式執行,如果當前線程存在事務,則掛起當前事務。
  • MANDATORY:如果當前線程存在事務,則加入該事務;如果不存在則拋出異常。
  • NEVER:以非事務方式執行,如果當前線程存在事務,則拋出異常。
  • NESTED:如果當前線程存在事務,則以嵌套事務中執行;如果不存在,則創建一個新事務。

這兒為什么強調線程呢?

方法間調用都在某個線程的方法棧中,按FILO順序執行。如果兩個方法的中sql使用兩個不同的數據庫連接執行,顯然無法納入一個事務中。

因為,數據庫連接必須能夠跨方法傳遞,Spring底層就是將connection放到ThreadLocal中。

2.2 傳播機制的實現

2.2.1 線程綁定連接

在DataSourceTransactionManager#doBegin中:

  • 從DataSource獲取數據連接connection,設置autocommit=false、隔離級別、超時時間等屬性;
  • 將connection放入ThreadLocal<Map>,Map的key是DataSource對象,value是connectionHolder對象。

可見,方法間調用時可以從ThreadLocal中拿到同一個連接,去執行不同的SQL,進而一同提交或回滾。

2.2.2 處理傳播機制

真正執行被代理對象方法前,會判斷是否創建事務。

調用AbstractPlatformTransactionManager#getTransaction,邏輯如下:

  1. 創建DataSourceTransactionObject對象,從ThreadLocal中獲取connectionHolder(可能為null);
  2. 當connectionHolder不為null且connectionHolder.transactionActive=ture時,說明已存在事務:

如果當前線程中存在事務:

  • 如果當前方法傳播行為是PROPAGATION_NEVER,則拋異常
  • 如果是PROPAGATION_NOT_SUPPORTED,則掛起當前事務,用一個新連接的非事務方式執行當前方法;
  • 如果是PROPAGATION_REQUIRES_NEW,則掛起當前事務,開啟一個新事務(獲取新連接并帶事務執行);
  • 如果是PROPAGATION_NESTED,則先設置savepoints(可以回滾到此處),然后使用同一個連接繼續執行。

如果當前線程中不存在事務:

  • 如果傳播行為是PROPAGATION_MANDATORY,則拋異常
  • 如果傳播行為是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED,則開啟事務

流程圖如下:

三 總結

  1. Spring中,對于事務這一抽象概念,從多個方法進行了良好封裝,如將隔離級別、超時時間等封裝為TransactionDefinition,將事務狀態、是否回滾等封裝為TransactionStatus。
  2. 事務的傳播行為,發生在方法間調用中。通過將connectionHolder放入ThreadLocal,實現了不同方法中使用同一數據庫連接,從而支持多種傳播方式。
  3. 事務底層,就是通過設置connection.autocommit為false,從而根據方法是否異常,選擇commit還是rollback;
  4. 通過Savepoint實現嵌套事務(需要數據庫支持)。
  5. 在執行某個方法時,判斷當前是否已經存在事務,就是判斷當前線程的ThreadLocal中是否存在一個數據庫連接對象。

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

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

相關文章

邏輯函數的神經網絡實現

1.單層感知器實現基本邏輯函數 先給大家拋出一道例題 &#xff08;一&#xff09;種類 a.OR函數 目標&#xff1a;當至少一個輸入為1時&#xff0c;輸出1&#xff1b;否則輸出0。 權重設置&#xff1a; 輸入權重&#xff1a;所有 wi1&#xff08;i1,2,...,m&#xff09;。…

SF-HCI-SAP問題收集1

最近在做HCI的集成&#xff0c;是S4的環境&#xff0c;發現很多東西都跑不通&#xff0c;今天開始收集一下錯誤點 如果下圖沖從0001變成0010&#xff0c;sfiom_rprq_osi表就會存數據&#xff0c;系統檢查到此表就會報錯&#xff0c;這個選項的作用就是自定義信息類型也能更新&a…

(面試經典問題之分布式鎖)分布式鎖的基本原理、作用以及實現

一、什么是分布式鎖 分布式鎖指的是在分布式場景中實現互斥類型的鎖。 分布式是什么意思&#xff1f;分布式表示運行的節點可能在不同的機器或不同的網段中&#xff0c;節點間通信通過socket。互斥類型是什么意思&#xff1f;互斥類型表示同一時刻只允許一個執行體進入臨界資…

機械硬盤與固態硬盤的區別-機械硬盤的未來在哪里?

隨著近年來固態硬盤的技術成熟和成本的下探&#xff0c;固態硬盤&#xff08;SSD&#xff09;儼然有要取代傳統機械硬盤&#xff08;HDD&#xff09;的趨勢&#xff0c;但目前單位容量下機械硬盤每GB價格相比閃存還有5-7倍的優勢&#xff0c;那么機械硬盤是否已經發展到極限&am…

06排序 + 查找(D1_排序(D1_基礎學習))

目錄 學習預熱&#xff1a;基礎知識 一、什么是排序 二、為什么要排序 三、排序的穩定性 四、排序穩定性的意義 五、排序分類方式 方式一&#xff1a;內外分類 方式二&#xff1a;比較分類 六、排序算法性能評估 1. 算法的時間復雜度 2. 算法的空間復雜度 七、知識小…

簡訊:Rust 2024 edition and v1.85.0 已發布

詳見 https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html 升級方法&#xff1a;rustup update stable

Python 錯誤和異常處理

目錄 try-except塊 例子&#xff1a; 輸出&#xff1a; 捕獲多種異常 例子&#xff1a; else和finally 例子&#xff1a; 輸出&#xff1a; 自定義異常 例子&#xff1a; 輸出&#xff1a; 好的&#xff0c;簡單來說&#xff0c;錯誤和異常處理是編程中用來處理程序…

Linux系統使用Docker部署Geoserver并做數據掛載進行地圖服務的發布和游覽

文章目錄 1、前提環境2、拉取geoserver鏡像3、創建數據掛載目錄4、 運行容器5、 測試使用&#xff08;發布shp數據為服務&#xff09;5.1、創建工作區5.2、添加數據存儲5.3、發布圖層5.4、服務游覽 1、前提環境 部署環境&#xff1a;Linux&#xff0c;Centos7 &#xff0c;Doc…

Innovus中快速獲取timing path邏輯深度的golden腳本

在實際項目中我們經常會遇到一條timing path級數特別多&#xff0c;可能是一兩頁都翻不完。此時&#xff0c;我們大都需要手工去數這條path上到底有哪些是設計本身的邏輯&#xff0c;哪些是PR工具插入的buffer和inverter。 數字IC后端手把手培訓教程 | Clock Gating相關clock …

Python爬蟲實戰:從零到一構建數據采集系統

文章目錄 前言一、準備工作1.1 環境配置1.2 選擇目標網站 二、爬蟲實現步驟2.1 獲取網頁內容2.2 解析HTML2.3 數據保存 三、完整代碼示例四、優化與擴展4.1 反爬應對策略4.2 動態頁面處理4.3 數據可視化擴展 五、注意事項六、總結互動環節 前言 在大數據時代&#xff0c;數據采…

SpringBoot中實現限流和熔斷功能

我們將使用Java的ScheduledExecutorService來實現一個簡單的令牌桶算法(Token Bucket Algorithm),并結合一個自定義的服務類來處理第三方API調用。 1. 創建限流器 首先,創建一個簡單的限流器類: import java.util.concurrent.*;public class SimpleRateLimiter {

如何使用Python快速開發一個帶管理系統界面的網站-解析方案

如果你想用 Python 開發一個 管理系統界面 的網站&#xff0c;并且希望界面美觀&#xff0c;可以考慮以下幾個框架和庫&#xff1a; 1. Streamlit&#xff08;快速、簡潔&#xff09; 適合&#xff1a;數據分析、儀表盤、內部管理系統特點&#xff1a; 寫法簡單&#xff0c;類…

Git常見命令--助力開發

git常見命令&#xff1a; 創建初始化倉庫&#xff1a; git 將文件提交到暫存區 git add 文件名 將文件提交到工作區 git commit -m "注釋&#xff08;例如這是發行的版本1&#xff09;" 文件名 查看狀態 如果暫存區沒有文件被提交顯示&#xff1a; $ git status On…

Java 前后端時間格式轉換

在 Web 開發里&#xff0c;時間格式處理既常見又關鍵。由于前端和后端對時間的表示、處理方式存在差異&#xff0c;熟練掌握時間格式的轉換方法就顯得尤為重要。這篇文章會深入探討 Java 前后端時間格式轉換的相關知識&#xff0c;特別是 Java 時間轉換的多種方式&#xff0c;其…

MySQL要點總結一

大綱 一.InnoDB的內存結構和更新機制 二.InnoDB的存儲模型 三.并發事務原理 四.索引原理和索引優化 一.InnoDB的內存模型 1.SQL的執行流程 2.InnoDB的內存模型 3.Buffer Pool中的空閑緩存頁與free鏈表 4.Buffer Pool中的臟頁和flush鏈表 5.Buffer Pool通過LRU鏈表來淘…

常用網絡工具分析(ping,tcpdump等)

寫在前面 本文看下常用網絡工具。 1&#xff1a;ping 1.1&#xff1a;用途 用于檢驗網絡的連通性。 1.2&#xff1a;實戰 在Linux環境中執行&#xff1a;ping www.sina.com.cn&#xff1a; [rootlocalhost ~]# ping www.sina.com.cn PING spool.grid.sinaedge.com (111.…

基于Flask的第七次人口普查數據分析系統的設計與實現

【Flask】基于Flask的第七次人口普查數據分析系統的設計與實現&#xff08;完整系統源碼開發筆記詳細部署教程&#xff09;? 目錄 一、項目簡介二、項目界面展示三、項目視頻展示 一、項目簡介 基于Flask的人口普查可視化分析系統 二、項目界面展示 登錄/注冊 首頁/詳情 …

11.Docker 之分布式倉庫 Harbor

Docker 之分布式倉庫 Harbor Docker 之分布式倉庫 Harbor1. Harbor 組成2. 安裝 Harbor Docker 之分布式倉庫 Harbor Harbor 是一個用于存儲和分發 Docker 鏡像的企業級 Registry 服務器&#xff0c;由 VMware 開源&#xff0c;其通過添加一些企業必需的功能特性&#xff0c;例…

Zookeeper應用案例-分布式鎖-實現思路

以下是具體實現代碼 第一步&#xff1a;注冊鎖節點 第二步&#xff1a;獲取鎖節點&#xff0c;如果自己是最小的節點&#xff0c;就獲取權限 第三步&#xff1a;拿到鎖就開始自己的業務邏輯 第四步&#xff1a;業務邏輯好了就要釋放這把鎖 第五步&#xff1a;重新注冊監聽&…

Elasticsearch7.1.1 配置密碼和SSL證書

生成SSL證書 ./elasticsearch-certutil ca -out config/certs/elastic-certificates.p12 -pass 我這里沒有設置ssl證書密碼&#xff0c;如果需要設置密碼&#xff0c;需要再配置給elasticsearch 在之前的步驟中&#xff0c;如果我們對elastic-certificates.p12 文件配置了密碼…