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 语言中包的定义、规…