SpringBoot + MyBatis 事務管理全解析:從 @Transactional 到 JDBC Connection 的旅程

SpringBoot + MyBatis 事務管理全解析:從 @Transactional 到 JDBC Connection 的旅程

  • 一、JDBC Connection:事務操作的真正執行者
    • 1.1 數據庫事務的本質
    • 1.2 Spring 與 Connection 的協作流程
  • 二、從 @Transactional 到 JDBC Connection 的完整鏈路
    • 2.1 Spring 中 TransactionInterceptor 的核心邏輯
    • 2.2 TransactionInterceptor 到 DataSourceTransactionManager 的調用鏈路
    • 2.3 DataSourceTransactionManager 的核心實現
      • 2.3.1 doBegin 方法:開啟事務并綁定資源
      • 2.3.2 doCommit 方法:提交事務
      • 2.3.3 doRollback 方法:回滾事務
    • 2.4 TransactionSynchronizationManager:線程級事務上下文管理
  • 三、MyBatis 與 Spring 事務的協作機制
    • 3.1 SqlSessionTemplate:Spring 環境下的 MyBatis 會話
    • 3.2 獲取 Spring 管理的 Connection
  • 四、完整鏈路總結:從注解到數據庫的七步旅程

在這里插入圖片描述

開篇:當我們使用 @Transactional 時,背后發生了什么?

SpringBoot + MyBatis 的項目中,只需在 Service 方法上添加@Transactional注解,就能輕松實現事務管理。但這個過程中,Spring 如何與 MyBatis 協作事務的提交 / 回滾究竟由誰執行?本文將基于spring-tx 5.3.23spring-boot-starter 2.2.2版本,深入剖析從注解到數據庫的完整鏈路。

一、JDBC Connection:事務操作的真正執行者

1.1 數據庫事務的本質

JDBC 規范中,所有事務操作都由Connection接口定義:

// java.sql.Connection接口核心方法
void setAutoCommit(boolean autoCommit) throws SQLException; // 開啟/關閉自動提交
void commit() throws SQLException; // 提交事務
void rollback() throws SQLException; // 回滾事務

無論上層框架如何封裝,最終執行事務提交 / 回滾的永遠是 JDBCConnection 對象Spring 的事務管理,本質是對這些底層操作的封裝與流程控制。

1.2 Spring 與 Connection 的協作流程

Spring 通過DataSourceTransactionManager管理 Connection 的生命周期,關鍵流程如下:

  1. 獲取連接:從數據源 (DataSource) 獲取 Connection
  2. 開啟事務:調用connection.setAutoCommit(false)
  3. 執行業務邏輯MyBatis 使用該 Connection 執行 SQL
  4. 提交 / 回滾:根據執行結果調用connection.commit()connection.rollback()
  5. 釋放連接:將 Connection 返回給連接池

偽代碼展示Spring管理Connection的核心邏輯:

// 偽代碼展示Spring管理Connection的核心邏輯
try {// 1. 從數據源獲取ConnectionConnection conn = dataSource.getConnection();// 2. 關閉自動提交,開啟事務conn.setAutoCommit(false);try {// 3. 執行SQL操作(MyBatis使用此Connection)userMapper.insert(user);orderMapper.createOrder(order);// 4. 提交事務conn.commit();} catch (Exception e) {// 5. 異常時回滾事務conn.rollback();} finally {// 6. 釋放連接conn.close(); // 實際由連接池管理}
} catch (SQLException ex) {throw new RuntimeException("數據庫操作失敗", ex);
}

二、從 @Transactional 到 JDBC Connection 的完整鏈路

2.1 Spring 中 TransactionInterceptor 的核心邏輯

TransactionInterceptorSpring 框架中專門用于攔截帶有 @Transactional 注解方法的 AOP 攔截器

TransactionInterceptor 類繼承自 TransactionAspectSupport,并實現了 MethodInterceptor 接口。在 Spring 的事務自動代理機制中,@Transactional 注解會被 TransactionAttributeSource 解析,最終觸發 TransactionInterceptor 的攔截邏輯

Spring 5.3.23 版本中,TransactionInterceptor的核心邏輯如下:

/*** AOP 方法攔截器的核心實現,用于在事務環境中執行目標方法*/
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {// TransactionAttributeSource 需要同時傳入目標類和方法(方法可能來自接口)Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// 委托給 TransactionAspectSupport 的核心事務處理方法。傳入目標方法、目標類和自定義的調用回調return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {/*** 繼續執行攔截鏈,最終會調用目標方法*/@Override@Nullablepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}@Overridepublic Object getTarget() {return invocation.getThis();}@Overridepublic Object[] getArguments() {return invocation.getArguments();}});
}

invoke 方法:

  • 作為 AOP 攔截器的入口,負責攔截方法調用
  • 解析目標類和方法信息
  • 創建回調接口,連接事務管理器和目標方法

invokeWithinTransaction 方法:

  • 事務管理的核心實現
  • 根據事務屬性配置創建事務
  • 執行目標方法并處理返回值
  • 根據執行結果決定提交或回滾事務

2.2 TransactionInterceptor 到 DataSourceTransactionManager 的調用鏈路

整個調用鏈路可分為以下關鍵步驟:

// 關鍵調用鏈路偽代碼
TransactionInterceptor.invoke()TransactionAspectSupport.invokeWithinTransaction()createTransactionIfNecessary() // 創建事務AbstractPlatformTransactionManager.getTransaction()DataSourceTransactionManager.doBegin() // 開啟事務→ invocation.proceedWithInvocation(); // 執行目標方法(包含MyBatis SQL)commitTransactionAfterReturning() // 正常返回后提交AbstractPlatformTransactionManager.commit()DataSourceTransactionManager.doCommit()completeTransactionAfterThrowing() // 異常時回滾AbstractPlatformTransactionManager.rollback()DataSourceTransactionManager.doRollback()

2.3 DataSourceTransactionManager 的核心實現

2.3.1 doBegin 方法:開啟事務并綁定資源

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {// 1. 獲取或創建新的Connectionif (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = obtainDataSource().getConnection();if (logger.isDebugEnabled()) {logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");}txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}// 2. 準備Connection用于事務txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();// 3. 設置隔離級別Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);txObject.setReadOnly(definition.isReadOnly());// 4. 【關鍵】:關閉自動提交,開啟事務if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}// 5. 準備事務同步prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);// 6. 超時設置int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// 7. 【關鍵】:將ConnectionHolder綁定到當前線程	if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {// 異常處理...}
}

關鍵步驟解析:

  • 步驟 4:調用con.setAutoCommit(false)開啟事務模式
  • 步驟 7:通過TransactionSynchronizationManager.bindResource()Connection 綁定到當前線程

2.3.2 doCommit 方法:提交事務

protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();try {// 核心:調用JDBC Connection的commit方法con.commit();}catch (SQLException ex) {throw new TransactionSystemException("Could not commit JDBC transaction", ex);}
}

2.3.3 doRollback 方法:回滾事務

protected void doRollback(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();try {// 核心:調用JDBC Connection的rollback方法con.rollback();}catch (SQLException ex) {throw new TransactionSystemException("Could not roll back JDBC transaction", ex);}
}

2.4 TransactionSynchronizationManager:線程級事務上下文管理

TransactionSynchronizationManagerSpring 事務管理的核心組件,使用ThreadLocal存儲當前線程的事務資源:

// org.springframework.transaction.support.TransactionSynchronizationManager (Spring 5.3.23)
private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<>("Current transaction name");// 綁定資源到當前線程
public static void bindResource(Object key, Object value) throws IllegalStateException {Map<Object, Object> map = resources.get();if (map == null) {map = new HashMap<>();resources.set(map);}Object oldValue = map.put(key, value);if (oldValue != null) {throw new IllegalStateException("Already value for key [" + key + "]");}
}// 從當前線程獲取資源
public static Object getResource(Object key) {Map<Object, Object> map = resources.get();return (map != null ? map.get(key) : null);
}

關鍵綁定點:
DataSourceTransactionManager.doBegin()方法中,通過以下代碼將 ConnectionHolder 綁定到當前線程:

TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());

三、MyBatis 與 Spring 事務的協作機制

3.1 SqlSessionTemplate:Spring 環境下的 MyBatis 會話

SqlSessionTemplateSpringMyBatis 集成的核心組件,它會優先使用 Spring 管理的事務連接:

執行 SQL 的核心方法是通過動態代理實現的。具體來說,所有 SQL 操作都會被代理到SqlSessionInterceptor類的invoke方法中處理。這個方法會獲取一個 SqlSession 實例,并調用其對應的 SQL 執行方法(如selectOneinsertupdate等)

private class SqlSessionInterceptor implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1. 從Spring事務上下文中獲取SqlSession(或創建新的)SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,SqlSessionTemplate.this.executorType,SqlSessionTemplate.this.exceptionTranslator);try {// 2. 通過反射調用SqlSession的實際方法(如selectOne、insert等)Object result = method.invoke(sqlSession, args);// 3. 如果不是事務管理的SqlSession,則手動提交if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {sqlSession.commit(true);}return result;} catch (Throwable t) {// 異常處理...} finally {// 4. 關閉SqlSession(如果不是事務管理的)if (sqlSession != null) {closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);}}}
}

3.2 獲取 Spring 管理的 Connection

getSqlSession()方法最終會調用SqlSessionUtils工具類,嘗試從TransactionSynchronizationManager獲取當前事務上下文中的 SqlSession

/*** 獲取MyBatis的SqlSession實例,支持事務同步管理*/
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {// 參數校驗...// 從當前事務同步管理器中獲取已綁定的SqlSession資源// 【核心邏輯】:事務中的SqlSession會綁定到當前線程SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);// 從現有持有者中獲取SqlSession(優先使用已存在的會話)SqlSession session = sessionHolder(executorType, holder);if (session != null) {return session;}// 調用MyBatis工廠方法創建新會話(指定執行器類型)session = sessionFactory.openSession(executorType);// 注冊SqlSession到事務同步管理器(關鍵邏輯:實現事務內會話共享)registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);return session;}

四、完整鏈路總結:從注解到數據庫的七步旅程

  1. 注解解析
    Spring 通過@Transactional注解獲取事務屬性配置
  2. AOP 攔截
    TransactionInterceptor攔截目標方法調用
  3. 事務管理器獲取
    根據配置獲取DataSourceTransactionManager實例
  4. 開啟事務
    調用doBegin()
    • 從數據源獲取 Connection
    • 設置autoCommit=false
    • Connection 綁定到TransactionSynchronizationManager
  5. 執行 SQL
    MyBatis 通過SqlSessionTemplate獲取 Spring 管理的 Connection 執行 SQL
  6. 提交 / 回滾事務
    根據執行結果調用doCommit()doRollback(),最終調用 Connection 的對應方法
  7. 資源清理
    釋放 Connection,解除與當前線程的綁定

理解 SpringMyBatis 的事務協作機制,不僅能幫助我們正確使用事務,更能在遇到問題時快速定位和解決。完結撒花(*^▽^)!!!*

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

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

相關文章

Wpf之應用圖標的修改!

前言 Wpf之應用圖標的修改&#xff01; 一、修改步驟 1、準備好ico圖片。 2、右鍵項目》點擊屬性 3、找到win32資源點擊 4、點擊瀏覽找到ioc圖標 5、點擊運行程序 6、右鍵項目點擊打開在資源管理器中打開 找到以下路徑 在該路徑下能看到.exe文件的圖標已經改成你想要的…

Spring Boot整合Redis指南

一、環境準備 在開始整合前&#xff0c;請確保已完成以下準備工作&#xff1a; 已安裝Redis服務&#xff08;安裝指南&#xff09;創建好Spring Boot項目 二、添加依賴 在項目的pom.xml中添加以下依賴&#xff1a; <!-- Redis核心依賴 --> <dependency><gr…

Re-攻防世界

easyEZbaby_app Jadx 這個文件一般是窗口界面&#xff0c;點擊中間的一般就是主函數 Obj1是用戶名&#xff0c;obj2是密碼 用戶名 public boolean checkUsername(String str) { if (str ! null) { try { if (str.length() ! 0 &&…

矩陣題解——搜索二維矩陣 II【LeetCode】

240. 搜索二維矩陣 II 1.1 核心思想 問題描述&#xff1a;給定一個 m x n 的二維矩陣&#xff0c;矩陣的每一行從左到右遞增&#xff0c;每一列從上到下遞增。判斷目標值 target 是否存在于矩陣中。解決思路&#xff1a; 從矩陣的右上角&#xff08;或左下角&#xff09;開始搜…

dockerfile文件詳解之基礎語法

dockerfile文件詳解之基礎語法 一般而言 Dockerfile 可以分為4個部分 &#xff08;1&#xff09;基礎鏡像信息&#xff0c; &#xff08;2&#xff09;維護者信息 &#xff08;3&#xff09;鏡像操作命令 &#xff08;4&#xff09;啟動時執行指令 1-注釋 用 # 來進行注…

WebFuture:獨立一級域名nginx取消配置Secure屬性的問題

問題分析&#xff1a; 部分站群站點使用了獨立一級域名&#xff0c;但是前臺問卷調查等模塊無法提交&#xff0c;排查是由于主站啟用了https&#xff0c;配置了cookies的Secure屬性是true&#xff0c;但是子站的獨立一級域名沒有使用https&#xff0c;所以瀏覽器不能寫入cooki…

【網站內容安全檢測】之3:獲取所有外部域名訪問后圖像

Go語言調用Chrome瀏覽器去進行截圖的操作&#xff0c;對電腦的性能要求比較高&#xff0c;所以速度比較有限&#xff0c;但是目前來看這種方式可以最佳的去獲取網頁加載后的結果。 main.go package mainimport ("context""errors""flag""…

華曦達港股IPO遞表,AI Home生態構建智能生活新藍圖

在智能家居逐漸普及的當下&#xff0c;華曦達打造的AI Home生態為用戶提供了更智能、便捷的生活解決方案&#xff0c;在行業中展現出獨特優勢。 華曦達AI Home生態由AI Home系統平臺、AI Home基礎設施、AI Home設備以及可連接外部設備的開放式設備矩陣構成&#xff0c;是一個開…

java+vue+SpringBoo智慧農業專家遠程指導系統(程序+數據庫+報告+部署教程+答辯指導)

源代碼數據庫LW文檔&#xff08;1萬字以上&#xff09;開題報告答辯稿ppt部署教程代碼講解代碼時間修改工具 技術實現 開發語言&#xff1a;后端&#xff1a;Java 前端&#xff1a;vue框架&#xff1a;springboot數據庫&#xff1a;mysql 開發工具 JDK版本&#xff1a;JDK1.…

免費AI助手工具深度測評:Claude4本地化部署與實戰應用指南

免費AI助手工具深度測評&#xff1a;Claude4本地化部署與實戰應用指南 AI無限對話免費Rovo工具Claude4碾壓cursor和augment 前言 在AI工具日益普及的今天&#xff0c;大多數高質量的AI助手都需要付費訂閱或有使用限制。然而&#xff0c;最近發現了一款基于Claude 4的免費AI助手…

MCP瀏覽器工具:playwright、chrome-mcp

參考&#xff1a; https://github.com/microsoft/playwright-mcp https://github.com/hangwin/mcp-chrome chrome-mcp安裝需要額外安裝成瀏覽器插件 用cherrystudio v1.4.5測試 mcp配置&#xff1a; "chrome-mcp-server": {"name": "chrome-mcp-serve…

水利水電安全員考試不同等級的考試內容有哪些區別?

水利水電安全員考試一般分為企業主要負責人&#xff08;A 類&#xff09;、項目負責人&#xff08;B 類&#xff09;和專職安全生產管理人員&#xff08;C 類&#xff09;三個等級。不同等級的考試內容都包括安全生產知識和管理能力兩部分&#xff0c;但具體的側重點有所不同。…

關于USB模式的一些內容(附USB接口顏色釋義圖)

今天在處理工作中的事情的時候,突然有個產品的小伙伴來問關于USB的事情,順便給她簡單說了下。USB接口模式主要包括以下幾種:Host(主機模式)、Device(設備模式)、OTG(On-The-Go),以及較少使用的Accessory模式。以下是對這些模式的詳細說明、區別差異及應用場景: 1. H…

React中的ErrorBoundary

文章目錄 前言? 一、使用類組件實現 ErrorBoundary&#xff08;官方推薦方式&#xff09;用法示例&#xff1a; ? 二、用函數組件實現 ErrorBoundary&#xff08;借助 Hook react-error-boundary 庫&#xff09;1. 安裝 react-error-boundary2. 使用 ErrorBoundary 組件&…

歷年西北工業大學計算機保研上機真題

西北工業大學計算機保研上機真題 在線測評鏈接&#xff1a;https://pgcode.cn/problem 海倫公式求面積 題目描述 給定三角形的三條邊長 a a a, b b b, c c c&#xff0c;先判斷這三條邊是否能構成一個三角形。 如果不能構成三角形&#xff0c;輸出 N a N NaN NaN&#…

掃地機產品認證--黑名單制裁公司能否拿到美國產品準入許可(FCC認證)

掃地機產品認證–黑名單制裁公司能否拿到美國產品準入許可(FCC認證) 文章目錄 掃地機產品認證--黑名單制裁公司能否拿到美國產品準入許可(FCC認證)?? **一、核心限制規則**?? **二、企業需滿足的額外條件**??? **三、黑名單企業的應對可能性**?? **四、總結**產品認證…

數據結構復習2

第二章 線性表 2.1線性表的定義和基本操作 線性表&#xff1a;一種邏輯結構&#xff0c;表示數據元素之間的一對一線性關系&#xff08;如數組、鏈表、棧、隊列等&#xff09;。 2.1.1線性表的定義 線性表是具有相同數據類型的n(n>0)個數據元素的有限序列。 (其中n為表長…

空間轉錄組benchmark 相關 讀完scGPT spatial 和 空間單細胞基因乳房細胞數據集文章之后

文章目錄 ? 空間轉錄組測序方式總體劃分&#x1f9ec; 成像型空間轉錄組&#xff08;Imaging-based ST&#xff09;原理&#xff1a;技術代表 & 特點&#xff1a;優點&#xff1a;局限&#xff1a; &#x1f9ec; 測序型空間轉錄組&#xff08;Sequencing-based ST&#x…

清理華為云服務器內存使用率

這里寫自定義目錄標題 一、正確終止進程&#xff1a;不要帶尖括號二、看清楚誰“真吃”了內存三、臨時清掉緩存&#xff08;謹慎用&#xff09;四、長期優化1. 給系統加個 Swap2. 調整 MySQL 內存配置3. 水平&#xff0f;垂直擴容4. 告警 總結與下一步 華為云的“內存使用率”默…

Go 語言中的 package 和 go modules

1、package 的定義和導入 在任何大型軟件項目中&#xff0c;代碼的組織和管理都是至關重要的。Go 語言通過 包&#xff08;Package&#xff09; 的概念來解決這個問題&#xff0c;它不僅是代碼組織的基礎&#xff0c;也是代碼復用的關鍵。本文將深入探討 Go 語言中包的定義、規…