MyBatis事務(wù)管理模塊詳解
1 引言
在企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中,事務(wù)管理是保證數(shù)據(jù)一致性與完整性的核心機(jī)制。無(wú)論是銀行轉(zhuǎn)賬、訂單支付,還是庫(kù)存扣減,這些操作都需要保證要么全部成功,要么全部失敗,否則就會(huì)產(chǎn)生數(shù)據(jù)不一致的嚴(yán)重問(wèn)題。
在 Java 生態(tài)中,MyBatis 作為一款輕量級(jí) ORM 框架,因其靈活的 SQL 定制能力和較低的學(xué)習(xí)成本被廣泛應(yīng)用。然而,MyBatis 本身并不是一個(gè)“全棧”持久層框架,它并不直接管理數(shù)據(jù)源的生命周期,而是通過(guò)事務(wù)管理機(jī)制協(xié)調(diào) JDBC 連接的提交與回滾。
理解 MyBatis 的事務(wù)管理機(jī)制不僅有助于我們編寫正確、健壯的代碼,還能幫助我們?cè)谂c Spring 等框架整合時(shí),精準(zhǔn)定位事務(wù)相關(guān)問(wèn)題(如事務(wù)未生效、連接泄漏、跨數(shù)據(jù)源事務(wù)失敗等)。
本系列文章的目標(biāo)是從概念、實(shí)現(xiàn)、源碼、整合、優(yōu)化五個(gè)維度,深入剖析 MyBatis 的事務(wù)管理機(jī)制,并給出實(shí)戰(zhàn)中的最佳實(shí)踐。
1.1 為什么事務(wù)管理如此重要
數(shù)據(jù)庫(kù)事務(wù)(Database Transaction)是保證ACID 特性(原子性 Atomicity、一致性 Consistency、隔離性 Isolation、持久性 Durability)的基礎(chǔ)。在分布式和高并發(fā)系統(tǒng)中,事務(wù)問(wèn)題會(huì)被放大:
- 原子性失效:轉(zhuǎn)賬扣款成功但入賬失敗,導(dǎo)致資金丟失。
- 隔離性不足:并發(fā)讀寫導(dǎo)致臟讀、不可重復(fù)讀、幻讀。
- 持久性丟失:系統(tǒng)崩潰后事務(wù)未持久化的數(shù)據(jù)丟失。
MyBatis 的事務(wù)管理器相當(dāng)于一個(gè)交通指揮員,它決定何時(shí)讓 SQL 執(zhí)行、何時(shí)提交或回滾,保證數(shù)據(jù)的一致性與安全性。
1.2 本文的核心關(guān)注點(diǎn)
本博客將重點(diǎn)分析以下幾個(gè)方面:
- MyBatis 核心事務(wù)接口與實(shí)現(xiàn)類:Transaction、TransactionFactory、JdbcTransaction、ManagedTransaction 等。
- 兩種事務(wù)管理機(jī)制:JDBC 事務(wù)與 MANAGED 事務(wù)的差異與適用場(chǎng)景。
- 與 Spring 的整合:@Transactional 注解、TransactionSynchronizationManager 的作用。
- 源碼解析:通過(guò)調(diào)用鏈路、類圖和流程圖揭示 MyBatis 事務(wù)的底層實(shí)現(xiàn)邏輯。
- 常見(jiàn)問(wèn)題與解決方案:事務(wù)未生效、連接泄漏、多數(shù)據(jù)源事務(wù)。
- 性能優(yōu)化與最佳實(shí)踐:事務(wù)粒度控制、連接復(fù)用、隔離級(jí)別調(diào)整。
2 核心概念
在深入源碼分析之前,我們需要先厘清 MyBatis 事務(wù)管理的幾個(gè)核心概念。這不僅包括數(shù)據(jù)庫(kù)層面的事務(wù)定義,還涉及 MyBatis 在架構(gòu)上對(duì)事務(wù)的抽象與實(shí)現(xiàn)。
2.1 事務(wù)的定義與特性
事務(wù)(Transaction)是數(shù)據(jù)庫(kù)操作的一個(gè)邏輯單元,由一組 SQL 語(yǔ)句組成。這些語(yǔ)句要么全部成功提交,要么全部回滾撤銷,確保數(shù)據(jù)的一致性。事務(wù)必須滿足 ACID 四大特性:
- 原子性(Atomicity):事務(wù)中的所有操作要么全部成功,要么全部失敗回滾。
- 一致性(Consistency):事務(wù)執(zhí)行前后,數(shù)據(jù)必須處于一致?tīng)顟B(tài)。
- 隔離性(Isolation):并發(fā)事務(wù)之間相互隔離,互不干擾。
- 持久性(Durability):事務(wù)提交后,對(duì)數(shù)據(jù)的修改是永久性的。
2.2 MyBatis 中的事務(wù)抽象
MyBatis 將事務(wù)管理功能抽象為一個(gè)核心接口和若干實(shí)現(xiàn)類,主要包括:
- Transaction 接口:定義了事務(wù)的基本操作方法,如
commit()、rollback()、close()、getConnection()等。 - TransactionFactory 接口:用于創(chuàng)建具體的 Transaction 實(shí)例,支持通過(guò)不同配置生成不同類型的事務(wù)管理器。
- JdbcTransaction:基于 JDBC 原生 API 實(shí)現(xiàn)的事務(wù)管理器,直接調(diào)用
java.sql.Connection的commit()和rollback()方法。 - ManagedTransaction:受外部容器(如 JEE 容器、Spring)管理的事務(wù)管理器,不直接控制事務(wù)提交與回滾。
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}上面的 Transaction 接口就是 MyBatis 的事務(wù)核心抽象,所有事務(wù)管理器都必須實(shí)現(xiàn)該接口,以統(tǒng)一事務(wù)操作方式。
2.3 MyBatis 中的事務(wù)邊界
在 MyBatis 中,事務(wù)邊界主要由 SqlSession 控制:
- 開(kāi)啟事務(wù):獲取
SqlSession并連接數(shù)據(jù)庫(kù)。 - 提交事務(wù):調(diào)用
sqlSession.commit()。 - 回滾事務(wù):調(diào)用
sqlSession.rollback()。 - 關(guān)閉事務(wù):調(diào)用
sqlSession.close(),釋放連接。
如果與 Spring 整合,事務(wù)邊界的控制權(quán)則由 Spring 的 PlatformTransactionManager 及其實(shí)現(xiàn)類(如 DataSourceTransactionManager)接管。
2.4 概念小結(jié)
從架構(gòu)設(shè)計(jì)來(lái)看,MyBatis 的事務(wù)管理分為三個(gè)層次:
- 接口層:定義統(tǒng)一的事務(wù)操作標(biāo)準(zhǔn)(
Transaction、TransactionFactory)。 - 實(shí)現(xiàn)層:提供不同類型事務(wù)的具體實(shí)現(xiàn)(
JdbcTransaction、ManagedTransaction)。 - 調(diào)用層:由
SqlSession或 Spring 框架驅(qū)動(dòng)事務(wù)的開(kāi)啟、提交、回滾與關(guān)閉。
3 MyBatis 事務(wù)管理機(jī)制
MyBatis 提供了兩種核心事務(wù)管理機(jī)制:JDBC 事務(wù) 與 MANAGED 事務(wù)。它們的區(qū)別主要在于事務(wù)控制權(quán)的歸屬以及事務(wù)生命周期的管理方式。
3.1 JDBC 事務(wù)
JDBC 事務(wù) 是 MyBatis 默認(rèn)的事務(wù)管理方式,由 MyBatis 直接通過(guò) JDBC API 控制事務(wù)的提交與回滾。
- 實(shí)現(xiàn)類:
JdbcTransaction - 控制權(quán):MyBatis 內(nèi)部
- 適用場(chǎng)景:獨(dú)立使用 MyBatis 或不依賴外部容器時(shí)。
工作機(jī)制:
- MyBatis 獲取連接后,會(huì)將
autoCommit設(shè)置為false。 - 執(zhí)行 SQL 后,由調(diào)用方顯式觸發(fā)
commit()或rollback()。 - 事務(wù)結(jié)束后釋放連接回連接池。
示例代碼:
Transaction tx = new JdbcTransaction(dataSource, level, false);
try (Connection conn = tx.getConnection()) {
// 執(zhí)行 SQL
tx.commit();
} catch (SQLException e) {
tx.rollback();
} finally {
tx.close();
}優(yōu)點(diǎn):簡(jiǎn)單直接。 缺點(diǎn):無(wú)法與外部事務(wù)資源(如 JTA)協(xié)作。
3.2 MANAGED 事務(wù)
MANAGED 事務(wù) 不直接控制事務(wù)提交與回滾,交由外部容器(如 Spring、JEE 容器)管理。
- 實(shí)現(xiàn)類:
ManagedTransaction - 控制權(quán):外部容器
- 適用場(chǎng)景:Spring 管理事務(wù)、JEE 容器環(huán)境。
工作機(jī)制:
- MyBatis 從外部容器獲取連接。
- 不在 MyBatis 內(nèi)部執(zhí)行
commit()和rollback()。 - 可通過(guò)
closeConnection配置決定是否由 MyBatis 關(guān)閉連接。
配置示例:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>3.3 配置對(duì)比
<!-- JDBC 事務(wù)配置 -->
<transactionManager type="JDBC"/>
<!-- MANAGED 事務(wù)配置 -->
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>3.4 差異總結(jié)
| 對(duì)比項(xiàng) | JDBC 事務(wù) | MANAGED 事務(wù) |
|---|---|---|
| 控制權(quán) | MyBatis 內(nèi)部 | 外部容器 |
| 提交/回滾 | MyBatis 調(diào)用 commit()/rollback() | 外部容器負(fù)責(zé) |
| 連接關(guān)閉 | MyBatis 內(nèi)部 | 可由外部容器 |
| 場(chǎng)景 | 獨(dú)立 MyBatis 項(xiàng)目 | Spring、JEE 容器環(huán)境 |
4 MyBatis 與 Spring 的事務(wù)整合
在實(shí)際開(kāi)發(fā)中,MyBatis 很少單獨(dú)使用,更多是在 Spring 或 Spring Boot 框架中與其他數(shù)據(jù)訪問(wèn)技術(shù)(如 JPA、JDBC Template)共存。為了在多種數(shù)據(jù)訪問(wèn)方式間實(shí)現(xiàn)統(tǒng)一的事務(wù)控制,Spring 接管了 MyBatis 的事務(wù)管理。
4.1 整合的核心思想
MyBatis 原本通過(guò) Transaction 接口及其實(shí)現(xiàn)類(如 JdbcTransaction、ManagedTransaction)來(lái)管理事務(wù)。在與 Spring 整合后,事務(wù)的開(kāi)啟、提交、回滾等生命周期由 Spring 的 PlatformTransactionManager(通常是 DataSourceTransactionManager)全權(quán)負(fù)責(zé)。
4.2 核心組件
- SqlSessionFactoryBean:Spring 提供的工廠類,用于生成
SqlSessionFactory,并配置數(shù)據(jù)源、事務(wù)工廠等。 - SpringManagedTransactionFactory:Spring 對(duì) MyBatis 事務(wù)工廠的實(shí)現(xiàn),用于生成
SpringManagedTransaction,它會(huì)委托 Spring 管理事務(wù)。 - TransactionSynchronizationManager:Spring 的事務(wù)同步管理器,用于將數(shù)據(jù)庫(kù)連接綁定到當(dāng)前線程,實(shí)現(xiàn)事務(wù)上下文的共享。
4.3 事務(wù)接管流程
- 當(dāng)業(yè)務(wù)方法被
@Transactional標(biāo)記時(shí),Spring 的事務(wù)攔截器(TransactionInterceptor)會(huì)啟動(dòng)事務(wù)(通過(guò)PlatformTransactionManager)。 - 事務(wù)啟動(dòng)時(shí),
DataSourceTransactionManager會(huì)從連接池獲取連接,并綁定到TransactionSynchronizationManager。 - 當(dāng) MyBatis 執(zhí)行 SQL 時(shí),通過(guò)
SpringManagedTransaction從TransactionSynchronizationManager獲取當(dāng)前線程綁定的連接,而不是自己創(chuàng)建連接。 - 提交或回滾事務(wù)的時(shí)機(jī)由 Spring 控制,在方法執(zhí)行結(jié)束時(shí)統(tǒng)一處理。
流程圖示意:
@Transactional 方法調(diào)用
↓
TransactionInterceptor 攔截
↓
PlatformTransactionManager 啟動(dòng)事務(wù)
↓
綁定連接到 TransactionSynchronizationManager
↓
MyBatis 執(zhí)行 SQL(SpringManagedTransaction 獲取連接)
↓
方法結(jié)束,統(tǒng)一提交或回滾事務(wù)
4.4 配置示例(Spring Boot)
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return factoryBean.getObject();
}
}4.5 關(guān)鍵點(diǎn)總結(jié)
- Spring 整合 MyBatis 時(shí),
TransactionFactory必須設(shè)置為SpringManagedTransactionFactory。 - 所有事務(wù)邊界(開(kāi)始、提交、回滾)都由 Spring 控制。
- MyBatis 在 Spring 環(huán)境下不會(huì)主動(dòng)關(guān)閉連接,而是由 Spring 在事務(wù)結(jié)束時(shí)統(tǒng)一管理。
5 MyBatis 事務(wù)管理源碼解析
本節(jié)從源碼角度系統(tǒng)拆解 MyBatis 事務(wù)管理的關(guān)鍵接口與實(shí)現(xiàn),并補(bǔ)充與 Spring 整合時(shí)的調(diào)用鏈路。示例以 MyBatis 3.x 與 mybatis-spring 2.x 為藍(lán)本,代碼片段做了少量刪減與注釋以便理解。
5.1 總覽:類關(guān)系與模塊邊界
從宏觀上看,事務(wù)相關(guān)模塊可以分為四層:
- 接口抽象層:
Transaction、TransactionFactory - MyBatis 內(nèi)部實(shí)現(xiàn)層:
JdbcTransaction、ManagedTransaction、對(duì)應(yīng)的JdbcTransactionFactory、ManagedTransactionFactory - 執(zhí)行器交互層:
Executor(BaseExecutor、SimpleExecutor等)在執(zhí)行前后調(diào)用commit/rollback/close - Spring 整合層:
SpringManagedTransaction、SqlSessionTemplate、SqlSessionUtils、DataSourceTransactionManager、TransactionSynchronizationManager
類圖(簡(jiǎn)化版):
classDiagram
interface Transaction {
+getConnection() Connection
+commit()
+rollback()
+close()
+getTimeout() Integer
}
class TransactionFactory {
<<interface>>
+newTransaction(Connection)
+newTransaction(DataSource, level, autoCommit)
}
class JdbcTransaction
class ManagedTransaction
class JdbcTransactionFactory
class ManagedTransactionFactory
class SpringManagedTransaction
Transaction <|.. JdbcTransaction
Transaction <|.. ManagedTransaction
Transaction <|.. SpringManagedTransaction
TransactionFactory <|.. JdbcTransactionFactory
TransactionFactory <|.. ManagedTransactionFactory5.2Transaction接口逐行解讀
public interface Transaction {
Connection getConnection() throws SQLException; // 懶加載或直接返回當(dāng)前連接
void commit() throws SQLException; // 提交當(dāng)前連接的事務(wù)
void rollback() throws SQLException; // 回滾當(dāng)前連接的事務(wù)
void close() throws SQLException; // 歸還連接或?qū)嶋H關(guān)閉
Integer getTimeout() throws SQLException; // 可選的事務(wù)超時(shí)
}設(shè)計(jì)要點(diǎn):
- 連接懶加載:MyBatis 常在首次需要時(shí)創(chuàng)建/獲取連接,減少無(wú)謂占用。
- 統(tǒng)一生命周期:不關(guān)心連接來(lái)自何處(直連或容器),一律通過(guò)
Transaction抽象進(jìn)行提交/回滾/關(guān)閉。
5.3JdbcTransaction源碼要點(diǎn)
JdbcTransaction 直接基于 DataSource 與 Connection 控制事務(wù):
public class JdbcTransaction implements Transaction {
private final DataSource dataSource;
private Connection connection;
private final TransactionIsolationLevel level;
private final boolean autoCommit;
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
connection = dataSource.getConnection();
if (level != null) connection.setTransactionIsolation(level.getLevel());
setDesiredAutoCommit(autoCommit);
}
return connection;
}
private void setDesiredAutoCommit(boolean desired) throws SQLException {
if (connection.getAutoCommit() != desired) {
connection.setAutoCommit(desired); // JDBC 事務(wù)關(guān)鍵:autoCommit=false 以啟用顯式提交
}
}
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) connection.commit();
}
@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) connection.rollback();
}
@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit(); // 歸還連接前恢復(fù)狀態(tài)(對(duì)連接池很重要)
connection.close();
}
}
}關(guān)鍵點(diǎn):
- 隔離級(jí)別設(shè)置:僅在首次獲取連接時(shí)設(shè)置,避免重復(fù)調(diào)用帶來(lái)的開(kāi)銷。
- autoCommit 管控:通過(guò)
autoCommit=false啟用顯式事務(wù)邊界。 - 資源歸還:
close()前恢復(fù)autoCommit,防止影響連接池中后續(xù)租戶。
示例:獨(dú)立 MyBatis(非 Spring)使用
SqlSessionFactory factory = ...;
try (SqlSession session = factory.openSession(false)) { // 關(guān)閉自動(dòng)提交
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.insert(u1);
mapper.insert(u2);
session.commit();
} catch (Exception ex) {
session.rollback();
}5.4ManagedTransaction源碼要點(diǎn)
ManagedTransaction 交由外部容器管理提交與回滾:
public class ManagedTransaction implements Transaction {
private final DataSource dataSource;
private Connection connection;
private final boolean closeConnection; // 是否在 close() 時(shí)關(guān)閉連接
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
connection = dataSource.getConnection(); // 來(lái)自容器/代理
}
return connection;
}
@Override public void commit() {} // 空實(shí)現(xiàn),由外部容器負(fù)責(zé)
@Override public void rollback() {}
@Override public void close() throws SQLException {
if (closeConnection && connection != null) connection.close();
}
}關(guān)鍵點(diǎn):
- 不觸碰事務(wù)邊界:
commit/rollback空實(shí)現(xiàn),避免與容器沖突。 - 連接關(guān)閉策略:通過(guò)
closeConnection決定是否交回容器或由容器統(tǒng)一回收。
5.5TransactionFactory與具體工廠
TransactionFactory 把選擇權(quán)在配置期就確定下來(lái):
public interface TransactionFactory {
default void setProperties(Properties props) {}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit);
}兩個(gè)常用實(shí)現(xiàn):
JdbcTransactionFactory:創(chuàng)建JdbcTransactionManagedTransactionFactory:創(chuàng)建ManagedTransaction
XML 配置示例:
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">...</dataSource>
</environment>
</environments>5.6 執(zhí)行器如何驅(qū)動(dòng)事務(wù):BaseExecutor
MyBatis 的 Executor 負(fù)責(zé)發(fā)起 SQL 執(zhí)行,并在恰當(dāng)時(shí)機(jī)觸發(fā)事務(wù)提交/回滾/關(guān)閉:
public abstract class BaseExecutor implements Executor {
protected final Transaction transaction;
@Override
public void commit(boolean required) throws SQLException {
if (closed) throw new ExecutorException("Closed");
clearLocalCache();
if (required) {
transaction.commit(); // 委托給 Transaction 實(shí)現(xiàn)
}
}
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
clearLocalCache();
if (required) {
transaction.rollback();
}
}
}
@Override
public void close(boolean forceRollback) {
try {
try {
if (forceRollback) rollback(true);
} finally {
transaction.close();
}
} catch (SQLException e) { ... }
}
}要點(diǎn):
- 職責(zé)單一:執(zhí)行器并不直接操作
Connection,一切通過(guò)Transaction完成,保證可替換性。 - 緩存清理:提交/回滾前清空本地緩存,確保一致性。
5.7 與 Spring 整合的源碼鏈路
當(dāng)引入 mybatis-spring 后,調(diào)用鏈整合如下:
@Transactional→ AOP 代理 →TransactionInterceptorTransactionInterceptor使用PlatformTransactionManager(常見(jiàn):DataSourceTransactionManager)開(kāi)始事務(wù)DataSourceTransactionManager獲取連接并綁定到TransactionSynchronizationManager(線程上下文)- MyBatis 側(cè):
SqlSessionTemplate→SqlSessionUtils.getSqlSession→ 檢索/創(chuàng)建SqlSession SpringManagedTransaction調(diào)用DataSourceUtils.getConnection(dataSource)→ 從TransactionSynchronizationManager取出線程綁定連接- 方法結(jié)束時(shí)由 Spring 統(tǒng)一提交/回滾,最后釋放/歸還連接
關(guān)鍵類片段:
// mybatis-spring: SpringManagedTransaction#getConnection
public Connection getConnection() throws SQLException {
if (this.connection == null) {
this.connection = DataSourceUtils.getConnection(this.dataSource);
}
return this.connection;
}
// Spring: DataSourceUtils.getConnection
public static Connection getConnection(DataSource ds) {
ConnectionHolder holder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(ds);
if (holder != null && holder.hasConnection()) {
return holder.getConnection();
}
Connection con = ds.getConnection();
// 如果處于事務(wù)中,包裝為 ConnectionHolder 并綁定到線程
// ...
return con;
}SqlSession 獲取與釋放(SqlSessionUtils):
public static SqlSession getSqlSession(SqlSessionFactory factory, ExecutorType type, PersistenceExceptionTranslator translator) {
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(factory);
if (holder != null) {
return holder.getSqlSession(); // 復(fù)用當(dāng)前事務(wù)內(nèi)的 SqlSession
}
SqlSession session = factory.openSession(type);
// 如果存在事務(wù),則注冊(cè)同步器,在事務(wù)完成時(shí)關(guān)閉 SqlSession
// TransactionSynchronizationManager.registerSynchronization(...)
return session;
}5.8 時(shí)序圖:@Transactional 方法的一次完整調(diào)用
sequenceDiagram participant C as Client participant S as @Transactional Service participant TI as TransactionInterceptor participant TM as DataSourceTransactionManager participant TSM as TransactionSynchronizationManager participant SUT as SqlSessionTemplate participant SMT as SpringManagedTransaction participant DS as DataSource/Pool participant DB as Database C->>S: 調(diào)用業(yè)務(wù)方法 S->>TI: AOP 攔截 TI->>TM: begin() 開(kāi)啟事務(wù) TM->>DS: getConnection() DS-->>TM: Connection TM->>TSM: 綁定 ConnectionHolder TI->>S: 繼續(xù)執(zhí)行業(yè)務(wù)邏輯 S->>SUT: 執(zhí)行 Mapper 方法 SUT->>SMT: getConnection() SMT->>TSM: 查詢線程綁定 Connection TSM-->>SMT: 返回同一 Connection SMT->>DB: 執(zhí)行 SQL SUT-->>S: 返回結(jié)果 S->>TI: 方法結(jié)束 TI->>TM: commit()/rollback() TM->>TSM: 清理并釋放連接 TM->>DS: 歸還連接到連接池
5.9 邊界條件與細(xì)節(jié)
- 多數(shù)據(jù)源:Spring 以
DataSource為 key 進(jìn)行資源綁定,若同一事務(wù)中使用多個(gè)數(shù)據(jù)源,會(huì)分別綁定多個(gè)ConnectionHolder。務(wù)必確保 Mapper 使用正確的數(shù)據(jù)源。 - 超時(shí)控制:
Transaction.getTimeout()可與 Spring 超時(shí)策略協(xié)同(如@Transactional(timeout=...))。 - 只讀事務(wù):Spring 在只讀場(chǎng)景可能降低隔離或優(yōu)化執(zhí)行計(jì)劃,但數(shù)據(jù)庫(kù)是否真正利用只讀標(biāo)志取決于驅(qū)動(dòng)/方言。
SqlSession復(fù)用:在事務(wù)內(nèi),同一SqlSessionFactory對(duì)應(yīng)單個(gè)SqlSession,跨線程不共享。
5.10 調(diào)試與定位建議
打開(kāi)日志:
# MyBatis 執(zhí)行與 JDBC 交互 logging.level.org.apache.ibatis=DEBUG logging.level.jdbc.sqlonly=DEBUG # Spring 事務(wù)與同步 logging.level.org.springframework.jdbc.datasource=DEBUG logging.level.org.springframework.transaction=DEBUG logging.level.org.mybatis.spring=DEBUG
打印連接標(biāo)識(shí):通過(guò)攔截器或日志模式打印 Connection#hashCode(),確認(rèn)同一事務(wù)內(nèi)是否為同一連接。
異常邊界:務(wù)必在 @Transactional 入口層拋出(或顯式標(biāo)注)需要回滾的異常類型,否則可能出現(xiàn)“方法拋異常但未回滾”的錯(cuò)覺(jué)。
6 MyBatis 事務(wù)管理常見(jiàn)問(wèn)題與解決方案
在實(shí)際項(xiàng)目中,MyBatis 的事務(wù)管理并非總是“開(kāi)箱即用”,很多問(wèn)題往往與配置、整合方式、事務(wù)傳播等相關(guān)。下面我們列出常見(jiàn)問(wèn)題、成因以及對(duì)應(yīng)解決方案。
6.1 事務(wù)未生效
問(wèn)題現(xiàn)象:@Transactional 注解標(biāo)記的方法中執(zhí)行多條 SQL,但數(shù)據(jù)庫(kù)最終只部分提交,或回滾未生效。
常見(jiàn)原因:
- 事務(wù)未被 Spring 管理:MyBatis 直接使用
SqlSessionFactory.openSession()獲取會(huì)話,繞過(guò)了 Spring 事務(wù)管理器。 - 方法調(diào)用未經(jīng)過(guò)代理:同類內(nèi)部方法調(diào)用不會(huì)觸發(fā) Spring AOP 攔截,事務(wù)邏輯失效。
- 事務(wù)傳播行為不匹配:如外層事務(wù)為
REQUIRES_NEW,內(nèi)層事務(wù)的回滾不影響外層事務(wù)。
解決方案:
- 確保通過(guò) Mapper 接口由 Spring 管理的 Bean 調(diào)用。
- 檢查
@Transactional注解位置,確保是public方法且通過(guò)代理調(diào)用。 - 根據(jù)業(yè)務(wù)需求正確配置
propagation屬性。
6.2 數(shù)據(jù)庫(kù)連接泄漏
問(wèn)題現(xiàn)象:系統(tǒng)長(zhǎng)時(shí)間運(yùn)行后,連接池中的連接耗盡,出現(xiàn) java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available。
常見(jiàn)原因:
- 事務(wù)或會(huì)話未正常關(guān)閉,連接未釋放回連接池。
- MANAGED 事務(wù)模式下
closeConnection配置錯(cuò)誤。
解決方案:
- 確保
SqlSession使用try-with-resources或在finally塊中關(guān)閉。 - 使用 Spring 時(shí)推薦
SpringManagedTransaction以自動(dòng)管理連接釋放。
6.3 多數(shù)據(jù)源事務(wù)問(wèn)題
問(wèn)題現(xiàn)象:跨多個(gè)數(shù)據(jù)源的操作無(wú)法在同一事務(wù)中提交或回滾。
常見(jiàn)原因:
- Spring 默認(rèn)的
DataSourceTransactionManager只支持單數(shù)據(jù)源。 - 不同數(shù)據(jù)源的連接未統(tǒng)一在同一事務(wù)上下文中。
解決方案:
- 使用
JtaTransactionManager或第三方分布式事務(wù)框架(如 Seata、Atomikos)。 - 對(duì)于強(qiáng)一致性要求不高的場(chǎng)景,可拆分為獨(dú)立事務(wù)處理。
6.4 嵌套事務(wù)與傳播行為異常
問(wèn)題現(xiàn)象:方法調(diào)用鏈中,內(nèi)層事務(wù)的回滾影響了外層事務(wù),或者預(yù)期的獨(dú)立事務(wù)沒(méi)有生效。
常見(jiàn)原因:
Spring 事務(wù)傳播屬性未按需求配置,如使用
REQUIRED導(dǎo)致內(nèi)外事務(wù)合并。
解決方案:
使用
REQUIRES_NEW保證內(nèi)層事務(wù)獨(dú)立執(zhí)行。使用
NESTED在支持 Savepoint 的數(shù)據(jù)庫(kù)中實(shí)現(xiàn)部分回滾。
6.5 MyBatis 緩存與事務(wù)提交
問(wèn)題現(xiàn)象:事務(wù)提交后,查詢結(jié)果仍是舊數(shù)據(jù)。
常見(jiàn)原因:
MyBatis 一級(jí)緩存、二級(jí)緩存未在事務(wù)提交時(shí)刷新。
解決方案:
在事務(wù)提交后調(diào)用
sqlSession.clearCache()刷新緩存。合理配置
flushCache屬性確保更新語(yǔ)句刷新緩存。
到此這篇關(guān)于MyBatis事務(wù)管理模塊詳解的文章就介紹到這了,更多相關(guān)MyBatis事務(wù)管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis事務(wù)原理與實(shí)戰(zhàn)深入解析
- MyBatisPlus+Spring實(shí)現(xiàn)聲明式事務(wù)的方法實(shí)現(xiàn)
- Atomikos + MybatisPlus解決多數(shù)據(jù)源事務(wù)一致性問(wèn)題解決
- MyBatisPlus中事務(wù)處理的實(shí)現(xiàn)
- Mybatis事務(wù)如何跟Spring結(jié)合(數(shù)據(jù)庫(kù)事務(wù)特性和Spring事務(wù)管理源碼)
- 一文搞懂?MyBatis的事務(wù)管理機(jī)制
- mysql+mybatis實(shí)現(xiàn)存儲(chǔ)過(guò)程+事務(wù)?+?多并發(fā)流水號(hào)獲取
相關(guān)文章
自己動(dòng)手編寫一個(gè)Mybatis插件之Mybatis脫敏插件
這篇文章主要介紹了自己動(dòng)手編寫一個(gè)Mybatis插件之Mybatis脫敏插件,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Nacos docker單機(jī)模式部署實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Nacos docker單機(jī)模式部署實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
sky-take-out項(xiàng)目中Redis的使用示例詳解
SpringCache是Spring的緩存抽象層,通過(guò)注解簡(jiǎn)化緩存管理,支持Redis等提供者,適用于方法結(jié)果緩存、更新和刪除操作,但無(wú)法實(shí)現(xiàn)Redis的高級(jí)功能(如數(shù)據(jù)結(jié)構(gòu)、事務(wù)、分布式鎖),本文給大家介紹sky-take-out項(xiàng)目中Redis的使用,感興趣的朋友一起看看吧2025-07-07
SpringBoot啟動(dòng)參數(shù)的實(shí)現(xiàn)
SpringBoot通過(guò)jar文件方式啟動(dòng),配置可以通過(guò)啟動(dòng)參數(shù)進(jìn)行覆蓋,本文就來(lái)介紹一下SpringBoot啟動(dòng)參數(shù)的實(shí)現(xiàn),感興趣的可以了解一下2025-01-01
SpringBoot框架DataSource多數(shù)據(jù)源配置方式
這篇文章主要介紹了SpringBoot框架DataSource多數(shù)據(jù)源配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
MongoDB整合Spring實(shí)例詳細(xì)講解(含代碼)
這篇文章主要介紹了MongoDB整合Spring實(shí)例詳細(xì)講解(含代碼),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
JavaWeb連接數(shù)據(jù)庫(kù)MySQL的操作技巧
數(shù)據(jù)庫(kù)是編程中重要的一部分,它囊括了數(shù)據(jù)操作,數(shù)據(jù)持久化等各方面。在每一門編程語(yǔ)言中都占有相當(dāng)大的比例。本次,小編以MySQL為例,使用mvc編程思想,給大家講解下javaweb對(duì)數(shù)據(jù)庫(kù)的操作2017-02-02
簡(jiǎn)單談?wù)凾hreadPoolExecutor線程池之submit方法
下面小編就為大家?guī)?lái)一篇簡(jiǎn)單談?wù)凾hreadPoolExecutor線程池之submit方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
Java 圖片復(fù)制功能實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Java 圖片復(fù)制功能實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10

