【Spring事物三千问】Spring的事务管理与MyBatis事物管理结合的原理

news2024/11/14 3:53:47

Spring的事务管理与MyBatis事物管理结合的原理

  • 前言
  • 正文
    • 原生的 MyBatis 的事务处理
      • SqlSession
      • Transaction
    • MyBatis 事务管理 与 Spring 事务管理的融合
      • SpringManagedTransaction
      • SqlSessionTemplate
      • SqlSessionInterceptor
      • mybatis-spring 融合的原理
    • 连接获取 & 开启事务
    • 连接的关闭/释放:并不是真正的关闭
    • MyBatis 中使用的 DB 连接 与 spring-tx 中使用的 DB 连接应该是同步的
    • 补充: Spring 与 MyBatis 结合后,为什么要调用两次 TransactionSynchronizationManager#bindSource()?
  • 小结

前言

spring-tx 是一套事务管理框架。
Mybatis 是一个 ORM 框架,它会操作 DB 连接来执行 sql,所以,也会涉及到事务的处理。
在 MyBatis 中,是通过 SqlSession 来执行 sql 的,也是通过它来管理事务的。默认情况下,MyBatis 是开启事务的,即: connection.setAutoCommit(false);

那么,spring-tx 的事务管理是如何与 mybatis 的事务管理进行整合的呢?

正文

通过 前面文章 的分析,我们知道 spring-tx 是通过 PlatformTransactionManager 来管理事务的。
查看 PlatformTransactionManager 源码,我们可以发现,它在底层是通过 org.springframework.jdbc.datasource.ConnectionHolder 中持有的连接来控制事务的。

下面我们先来研究一下 MyBatis 是如何进行事务管理的?

原生的 MyBatis 的事务处理

MyBatis 有两种方式来处理事务: 一是,SqlSession;二是,Transaction;

SqlSession

org.apache.ibatis.session.SqlSession:
The primary Java interface for working with MyBatis. Through this interface you can execute commands, get mappers and manage transactions.

SqlSession 是使用 MyBatis 时的关键接口。通过此接口,可以执行 sql 命令、获取 mappers 映射器和管理事务。
SqlSession 提供了 commit()/rollback()getConnection()/close() 方法来操作事物和管理连接。
SqlSession.png

SqlSession 操作事物的例子:

SqlSession session = sqlSessionFactory.openSession();
try {
    int affected_rows = session.insert("com.kvn.mapper.UserMapper.insert", user);
} catch (Exception e) {
    // 捕获到异常,将操作回滚
    session.rollback();
}
// 正常执行,提交事务
session.commit();
session.close();

查看 DefaultSqlSession 的源码,可以发现,MyBatis 的事务和连接管理最终是交给 org.apache.ibatis.transaction.Transaction 来处理的。

Transaction

org.apache.ibatis.transaction.Transaction:
Wraps a database connection. Handles the connection lifecycle that comprises: its creation, preparation, commit/rollback and close.

Transaction 会包装数据库的连接,处理数据库连接的生命周期,包括:连接的创建、准备、提交、回滚和关闭。

可以说,SqlSession 是一个偏向于 sql 执行和应用的接口,它既可以执行 sql,又可以对事物进行管理。而 SqlSession 操作事物时,底层是通过 Transaction 来实现的。
Transaction.png

Transaction 操作事物的例子:

public void doBiz(){
    TransactionFactory transactionFactory = new JdbcTransactionFactory();
    userMapper userDao=getSession().getMapper(UserMapper.class);
    Transaction newTransaction=transactionFactory.newTransaction(getSession().getConnection());
    try {
        userDao.insert(xxx);
        userDao.update(xxx);
    } catch (Exception e) {
        newTransaction.rollback();
        e.printStackTrace();
    } finally {
        newTransaction.close();
    }
}

综上,MyBatis 的事务管理,统一是通过 org.apache.ibatis.transaction.Transaction 来管理的。

MyBatis 事务管理 与 Spring 事务管理的融合

MyBatis 的事务和连接的管理是通过 mybatis-spring-2.0.3.jar 这个 jar 包来完成与 spring-tx 体系下的事务和连接管理整合的。

mybatisspring.png

SpringManagedTransaction

SpringManagedTransaction 实现了 org.apache.ibatis.transaction.Transaction 接口。
SpringManagedTransaction 打通了 MyBatis 的事物管理、连接管理 和 spring-tx 的 事物管理、连接管理,使得 MyBatis 与 Spring 可以使用统一的方式来管理连接的生命周期 和 事务处理。

这里要格外的注意一下
MyBatis 与 Spring 通过 mybatis-spring-2.0.3.jar 结合使用之后,事物的管理的逻辑就分为了两种情况:

    1. 在一个非 @Transactional 标记的方法中执行 sql 命令,则事物的管理会通过 SpringManagedTransaction 来执行。
    1. 在一个 @Transactional 标记的事物方法中执行 sql 命令,则 SpringManagedTransactioncommit()/rollback() 方法不会执行任何动作,而事物的管理会走 Spring 的 AOP 事物管理,即通过 org.springframework.transaction.interceptor.TransactionInterceptor 来进行拦截处理。

SpringManagedTransactioncode.png

SpringManagedTransaction.png

MyBatis 的事务管理是通过 SqlSession 来进行管理的,底层又是通过 org.apache.ibatis.transaction.Transaction 来进行管理的。
Spring 与 MyBatis 整合时,通过 SpringManagedTransaction 扩展了 Transaction 接口。这样,MyBatis 的事务管理就和 Spring 的事务管理结合在一起了。

SqlSessionTemplate

MyBatis 执行 sql 都是通过 SqlSession 接口来执行的。
MyBatis 与 Spring 结合之后,sql 的执行具体会通过实现类 org.mybatis.spring.SqlSessionTemplate 来完成。

SqlSessionTemplate 的构造函数如下:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  notNull(executorType, "Property 'executorType' is required");

  this.sqlSessionFactory = sqlSessionFactory;
  this.executorType = executorType;
  this.exceptionTranslator = exceptionTranslator;
  // 创建 SqlSession 的代理对象
  this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
      new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}

可以看出,SqlSessionTemplate 持有一个 sqlSessionProxy 属性。SqlSessionTemplate 相当于是 SqlSession 接口的一个代理,它将所有的 SqlSession 的方法执行委托给 sqlSessionProxy 去执行。
所有的方法执行都通过 SqlSessionInterceptor 去进行拦截。

SqlSessionInterceptor

SqlSessionInterceptor 会对每次执行 Mapper 查询时进行拦截,然后通过 Spring 的事务同步器获取到当前的 SqlSession 去执行 sql 操作。
这里是 MyBatis 的 SqlSession 与 spring-tx 结合的一个非常关键的点!

SqlSessionInterceptor 保证了 MyBatis 的 SqlSession 在执行 sql 时使用的连接与 Spring 事物管理操作使用的连接是同一个连接。具体就是通过 Spring 的事务同步器 TransactionSynchronizationManager 来保证的。
详细见下面的源码:
SqlSessionInterceptor.png

Spring 事务同步器 TransactionSynchronizationManager 会将连接资源绑定到 ThreadLocal 变量中,如果是在同一个事务当中的话,就可以通过 TransactionSynchronizationManager 中的 ThreadLocal 变量来获取到同一个连接资源。

mybatis-spring 融合的原理

针对上面一些核心类的分析可知:

  • mybatis-spring.jar 是通过 SqlSessionTemplate 来创建 SqlSession 的代理 sqlSessionProxy;
  • sqlSessionProxy 会通过 SqlSessionInterceptor 来对 SqlSession 中的每个 sql 操作进行拦截,从而使用 spring-tx 的事务同步器 TransactionSynchronizationManager 中管理的 SqlSession 来执行 sql。
  • 在执行 sql 前,是通过 SpringManagedTransaction 来获取连接和管理事物的。
  • 如果是 @Transactional 标记的事物方法,SpringManagedTransaction 就会放弃事物的管理,交由 spring-tx 的 TransactionInterceptor 来进行 aop 拦截,从而管理事物。

SpringManagedTransaction 中连接的获取是从 Spring 管理的 DataSource 中获取的,这样,数据库连接池也就和 spring 整合在一起了。

doGetConnection.png

连接获取 & 开启事务

mybatis 通过 org.apache.ibatis.transaction.Transaction 接口来获取连接和进行事务管理。
与 Spring 结合时,通过实现类 org.mybatis.spring.transaction.SpringManagedTransaction 来获取连接

mybatisTransaction.png

org.mybatis.spring.transaction.SpringManagedTransaction#getConnection() 的代码如下:

public Connection getConnection() throws SQLException {
  if (this.connection == null) {
    openConnection();
  }
  return this.connection;
}

/**
 * Gets a connection from Spring transaction manager and discovers if this {@code Transaction} should manage
 * connection or let it to Spring.
 * It also reads autocommit setting because when using Spring Transaction MyBatis thinks that autocommit is always
 * false and will always call commit/rollback so we need to no-op that calls.
 */
private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

    LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
    + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
}

连接的关闭/释放:并不是真正的关闭

org.mybatis.spring.transaction.SpringManagedTransaction#close()

/**
 * 关闭连接。最终也是委托给 DataSourceUtils#releaseConnection()  
 */
public void close() {
  DataSourceUtils.releaseConnection(this.connection, this.dataSource);
}

详细的调用过程如下:
closeConnection.png

MyBatis 中使用的 DB 连接 与 spring-tx 中使用的 DB 连接应该是同步的

MyBatis SqlSession 在执行 sql 时使用的 DB 连接,与 spring-tx 管理事物时使用的 DB 连接应该是同一个,这样才能达到事物管理的效果。

连接资源同步的处理流程如下:

  • org.mybatis.spring.SqlSessionUtils#registerSessionHolder()
    SqlSessionUtils#registerSessionHolder() 这个方法里面会调用 TransactionSynchronizationManager.bindResource(SqlSessionFactory, SqlSessionHolder); 将 DB 连接资源 SqlSessionHolder 绑定到当前线程。
    SqlSessionHolder 中获取 DB 连接最终会调用 SpringManagedTransaction#getConnection() 来获取,最终还是会优先从当前线程中绑定的 ConnectinHolder 当中来获取 DB 连接。

  • org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection()
    spring-tx 在通过 DataSourceUtils#doGetConnection() 获取到连接之后,会调用 TransactionSynchronizationManager.bindResource(dataSource, holderToUse),将连接包装成 ConnectinHolder 并绑定到当前线程中。

补充: Spring 与 MyBatis 结合后,为什么要调用两次 TransactionSynchronizationManager#bindSource()?

现象:
我们可以在代码中打印 TransactionSynchronizationManager.getResourceMap() ,可以发现里面有两个值:

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@168b41a=org.mybatis.spring.SqlSessionHolder@79b62800
HikariDataSource (HikariPool-1)=org.springframework.jdbc.datasource.ConnectionHolder@7c6e897d

这说明,spring-tx 执行了两次 bindSource() 操作。

疑问:spring 与 mybatis 结合使用时,为什么要调用两次 bindSource() 呢?
答:spring-tx 管理事物时,连接是通过 org.springframework.jdbc.datasource.ConnectionHolder 来持有的,为了方便获取连接,ConnectionHolder 是通过 bindSource() 操作绑定在当前线程中的。
MyBatis 的 连接获取 和 sql 操作是通过 SqlSession 来完成的,为了方便获取 SqlSession, mybatis-spring.jar 中通过 org.mybatis.spring.SqlSessionUtils#registerSessionHolder() 调用 bindSource(),将 SqlSessionHolder 绑定在了当前线程中。
SqlSession 在获取连接时,是通过 SpringManagedTransaction 来获取的,底层最终还是优先获取当前线程中绑定的 ConnectionHolder
这样, MyBatis 的事物管理在与 Spring 进行整合后,在处理 @Transactional 事物方法时,使用的就是同一个 Connection 了。

TransactionSynchronizationManager#bindSource() 将事务资源绑定在了线程变量 ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");

  • Spring 绑定事物资源的 key=DataSource实现类, value=ConnectionHolder
  • MyBatis 绑定事物资源的 key=DefaultSqlSessionFactory, value=SqlSessionHolder

参考: https://www.cnblogs.com/timfruit/p/11508873.html
要想将sql语句的执行由mybatis执行, 事务的提交或者回滚操作由Spring控制, 两者需要关联使用同一个connection, 在不同的方法中调用connection的相关方法操作, (所以, Spring并没有直接使用mybatis sqlSession中提供的提交或者回滚方法) . 如何安全的获取同一个connection?这就需要使用TransactionSynchronizationManager

Spring 没有直接使用 MyBatis 的 Transaction 中的事务管理的 begin/commit/rollback 方法,而是通过 SpringManagedTransaction 类中持有的 java.sql.Connection 对象直接进行事务管理的。

小结

JDBC 连接的生命周期分为: 连接的创建、提交、回滚和关闭
我们将连接的创建/关闭分为一组,叫连接的管理;将连接的 提交/回滚 分为一组,叫事物的管理


MyBatis 原生的 连接管理 和 事物管理 是交给 org.apache.ibatis.transaction.Transaction 来管理的。
Spring-tx 主要封装的是事物管理,事物管理操作是通过 DataSourceTransactionManager 来实现的。而连接的管理是通过 org.springframework.jdbc.datasource.DataSourceUtils 来操作具体的 DataSource 来实现的。


MyBatis 与 Spring-tx 的事物管理的整合是通过 mybatis-spring-2.0.3.jar 中的 SpringManagedTransaction 来完成的。
SpringManagedTransaction 打通了 MyBatis 的事物管理、连接管理 和 spring-tx 的 事物管理、连接管理,使得 MyBatis 与 Spring 可以使用统一的方式来管理连接的生命周期 和 事务处理。


MyBatis 与 Spring 结合之后,sql 的执行具体会通过实现类 org.mybatis.spring.SqlSessionTemplate 来完成。
SqlSessionTemplate 每次在执行 sql 时,都会被 SqlSessionInterceptor 进行拦截,拦截后会通过 Spring 的事务同步器 TransactionSynchronizationManager 获取到当前的 SqlSession 去执行 sql 操作。
SqlSessionInterceptor 保证了 MyBatis 的 SqlSession 在执行 sql 时使用的连接与 Spring 事物管理操作使用的连接是同一个连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/377043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】理解进程地址空间

&#x1f34e;作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;Linux系统编程 ​我们在学习C语言的时候&#xff0c;都学过内存区域的划分如栈、堆、代码区、数据区这些。但我们其实并不真正理解内存 — 我们之前一直说的内存是物理上的内存吗&#xff1f; 前言 我们…

18- TensorFlow实现CIFAR10分类 (tensorflow系列) (项目十八)

项目要点 导入cifar图片集: (train_image, train_label), (test_image, test_label) cifar.load_data() # cifar keras.datasets.cifar10图片归一化处理: train_image train_image / 255定义模型: model keras.Sequential() 输入层: model.add(layers.Conv2D(64, (3, 3…

HTML基础语法

一 前端简介构成语言说明结构HTML页面元素和内容表现CSS网页元素的外观和位置等页面样式&#xff08;美化&#xff09;行为JavaScript网页模型的定义和页面交互二 HTML1.简介HTML&#xff08;Hyper Text Markup Language&#xff09;&#xff1a;超文本标记语言。网页结构整体&…

Kubernetes05: Pod

Kubernetes05: Pod 1、概述 1&#xff09;最小部署的单元 2&#xff09;K8s不会处理容器&#xff0c;而是Pod&#xff0c;Pod里边包含多个容器&#xff08;一组容器的集合&#xff09; 3&#xff09;一个Pod中的容器共享一个网络命名空间 4) Pod是短暂存在的东西&#xff08;重…

使用shiroshiro整合其他组件

什么是shiro&#xff1f; 一款apache公司出品的Java安全框架&#xff0c;主要用于设计针对应用程序的保护&#xff0c;使用shiro可以完成认证、授权、加密、会话管理等。保证系统稳定性、数据安全性 优势&#xff1a;易于使用、易于理解、兼容性强&#xff08;可以与其他框架集…

SE-SSD论文阅读

摘要 本文提出了一种基于自集成单级目标检测器(SE-SSD)的室外点云三维目标检测方法。我们的重点是利用我们的公式约束开发软目标和硬目标来联合优化模型&#xff0c;而不引入额外的计算在推理中。具体来说&#xff0c;SE-SSD包含一对teacher 和student ssd&#xff0c;在其中我…

Mac 安装 Java 反编译工具 JD-GUI

Mac 安装 Java 反编译工具 JD-GUI JD-GUI 是一款 Java 反编译工具&#xff0c;可以方便的将编译好的 .class 文件反编译为 .java 源码文件&#xff0c;用于开发调试、源码学习等。 官网地址&#xff1a;http://java-decompiler.github.io Git 地址&#xff1a;https://github…

直播美颜sdk是什么?它是怎么让用户”变美“的?

如今&#xff0c;直播美颜sdk、手机摄影、短视频以及社交软件的盛行&#xff0c;让“拍照”成为人们日常生活中不可或缺的一部分。随着直播美颜sdk技术的不断升级&#xff0c;手机摄影的质量也越来越高。有统计数据显示&#xff0c;2018年中国智能手机用户已经达到了7亿人&…

美国最新调查显示 50% 企业已在用 ChatGPT,其中 48% 已让其代替员工,你怎么看?

美国企业开始使用ChatGPT&#xff0c;我认为这不是什么新闻。 如果美国的企业现在还不使用ChatGPT&#xff0c;那才是个大新闻。 据新闻源显示&#xff0c;已经使用chatGPT的企业中&#xff0c;48%已经让其代替员工工作。 ChatGPT的具体职责包括&#xff1a;客服、代码编写、招…

HTB-remote

HTB-remote信息搜集开机提权信息搜集 nmap 较为感兴趣的端口&#xff1a; 2180nfs 首先尝试21端口&#xff0c;可以看到并没有文件在ftp服务器里面&#xff0c;而且也无法上传文件。 80端口。 在contact里面找到了能够登录的网站。 经过简单的测试发现可能不存在sql注…

逆向、安全、工具集

0、安卓逆向环境 r0env 原味镜像介绍文章&#xff1a;https://mp.weixin.qq.com/s/gBdcaAx8EInRXPUGeJ5ljQ 原味镜像介绍视频&#xff1a;https://www.bilibili.com/video/BV1qQ4y1R7wW/ 百度盘&#xff1a;链接:https://pan.baidu.com/s/1anvG0Ol_qICt8u7q5_eQJw 提取码:3x2a …

【Spring源码】Spring AOP的核心概念

废话版什么是AOP关于什么是AOP&#xff0c;这里还是要简单介绍下AOP&#xff0c;Aspect Oriented Programming&#xff0c;面向切面编程&#xff0c;通过预编译和运行期间提供动态代理的方式实现程序功能的统一维护&#xff0c;使用AOP可以降低各个部分的耦合度&#xff0c;提高…

openfeign负载均衡策略 | Spring Cloud 5

一、Spring Cloud LoadBalancer介绍 Spring Cloud LoadBalancer是Spring Cloud官网提供的一个客户端负载均衡器&#xff0c;功能类似于Ribbon。在Spring Cloud Nacos 2021移除了中Ribbon组件&#xff0c;Spring Cloud在Spring Cloud Commons项目中&#xff0c;添加了Spring Cl…

华为OD机试题,用 Java 解【N 进制减法】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…

Linux | 分布式版本控制工具Git【版本管理 + 远程仓库克隆】

文章目录一、前言二、有关git的相关历史介绍三、Git版本管理1、感性理解 —— 大学生实验报告2、程序员与产品经理3、张三的CEO之路 —— 版本管理工具的诞生四、如何在Linux上使用Git1、创建仓库2、将仓库克隆到本地3、git三板斧① git add② git commit③ git push4、有关git…

yarn run serve报错Error: Cannot find module ‘@vue/cli-plugin-babel‘ 的解决办法

问题概述 关于这个问题&#xff0c;是在构建前端工程的时候遇到的&#xff0c;项目构建完成后&#xff0c;“yarn run serve”启动项目时&#xff0c;出现的问题&#xff1a;“ Error: Cannot find module ‘vue/cli-plugin-babel‘ ” 如下图&#xff1a; 具体信息如下&…

(24秋招笔试准备)回溯专题--代码随想录刷题记录

回溯算法理论基础回溯三部曲&#xff1a;编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;组合问题https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ组合总和https://mp.weixin.qq.com/s/HX7WW6ixbFZJASkRnCTC3whttps://mp.weixin.qq.com/…

Linux系统认知——驱动认知

文章目录一、驱动相关概念1.什么是驱动2.被驱动设备分类3.设备文件的主设备号和次设备号4.设备驱动整体调用过程二、基于框架编写驱动代码1.驱动代码框架2.驱动代码的编译和测试三、树莓派I/O口驱动的编写1.微机的总线地址、物理地址、虚拟地址介绍2.通过树莓派芯片手册确定需要…

zabbix部署

文章目录前言一、zabbix简介二、zabbix下载与部署三、部署完成、访问前端测试前言 一、zabbix简介 Zabbix 是一个企业级分布式开源监控解决方案。Zabbix 软件能够监控众多网络参数和服务器的健康度、完整性。Zabbix 使用灵活的告警机制&#xff0c;允许用户为几乎任何事件配置…

数据结构与算法——4时间复杂度分析(常见的大O阶)

这篇文章是时间复杂度分析的第二篇。在前一篇文章中&#xff0c;我们从0推导出了为什么要用时间复杂度&#xff0c;时间复杂度如何分析以及时间复杂度的表示三部分内容。这篇文章&#xff0c;是对一些常用的时间复杂度进行一个总结&#xff0c;相当于是一个小结论 1.常见的大O…