事务同步管理器TransactionSynchronizationManager

事务同步管理器的使用场景:

  • 同步涉及的资源包括:SqlSession & Connection。
  • 同步资源核心目的是线程共享,意味着必须跟线程绑定。
  • 同步资源伴随着线程生存或者消亡,意味着线程结束之前必须手动清除其绑定的资源。
  • 事务同步管理器提供的所有字段、方法均是静态属性,使用任何类型的对象【SqlSession、数据库连接Connection】。

事务同步管理器同时提供了一个接口TransactionSynchronization作为扩展点,通常情况下只需继承抽象适配类TransactionSynchronizationAdapter完成扩展。

public interface TransactionSynchronization{

	int STATUS_COMMITTED = 0;

	int STATUS_ROLLED_BACK = 1;

	int STATUS_UNKNOWN = 2;
	
	default void suspend() {
	}

	default void resume() {
	}

	default void flush() {
	}

	default void beforeCommit(boolean readOnly) {
	}

	default void beforeCompletion() {
	}

	default void afterCommit() {
	}

	default void afterCompletion(int status) {
	}

}

 如上所示该扩展点主要是在事务提交之前增加了扩展,方便用户控制事务提交前后的行为。

具体实现过程可以参考SqlSessionSynchronizationConnectionSynchronization


截止当前事务同步管理器处理的资源主要包括:DataSource获取的数据库连接以及SqlSessionFactory。

public abstract class TransactionSynchronizationManager {

	ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");

	ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");

    ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");

	
	public static Object getResource(Object key) {// key 可能是DataSource、SqlSessionFactory
		// 获取目标对象的key
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		// 优先从本地线程中获取目标对象
		Object value = doGetResource(actualKey);
		return value;
	}

	// 将目标对象与本地线程建立绑定关系
	public static void bindResource(Object key, Object value) throws IllegalStateException {
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Map<Object, Object> map = resources.get();
		if (map == null) {
			map = new HashMap<>();
			resources.set(map);
		}
		Object oldValue = map.put(actualKey, value);
	}

	// 解除 目标对象与本地线程的绑定关系
	public static Object unbindResource(Object key) throws IllegalStateException {
		Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
		Object value = doUnbindResource(actualKey);
		return value;
	}

	@Nullable
	private static Object doUnbindResource(Object actualKey) {
		Map<Object, Object> map = resources.get();
		if (map == null) {
			return null;
		}
		Object value = map.remove(actualKey);
		if (map.isEmpty()) {
			resources.remove();
		}
		if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
			value = null;
		}
		return value;
	}


	public static boolean isSynchronizationActive() {
		return (synchronizations.get() != null);
	}

	public static void initSynchronization() throws IllegalStateException {
		synchronizations.set(new LinkedHashSet<>());
	}

    public static void registerSynchronization(TransactionSynchronization synchronization){
	    Set<TransactionSynchronization> synchs = synchronizations.get();
	    synchs.add(synchronization);
    }

}

事务同步管理器涉及三个核心属性:resources、synchronizations、actualTransactionActive,这些属性都是跟线程存在绑定关系。

  • resources:线程绑定的资源。包括数据库连接Connection、SqlSession。
  • synchronizations:线程绑定TransactionSynchronization类型的资源。该资源绑定之前首先初始化Synchronization,其次判断是否被激活,最后才会被添加到集合类型属性synchronizations中。

1.数据库连接

不管应用中是否涉及事务,数据库连接的创建都是通过DataSourceUtils工具类完成的。

  • 如果没有事务则直接使用新建的自动提交特性的数据库连接,并且不需要与线程建立绑定关系。
  • 如果存在事务则提前初始化TransactionSynchronizationManager的属性synchronizations,之后就会将Connection交由ConnectionHolder持有,并且与线程建立绑定关系,最后初始化ConnectionSynchronization实例。
public abstract class DataSourceUtils {
	
	public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		return doGetConnection(dataSource);
	}

	public static Connection doGetConnection(DataSource dataSource) throws SQLException {

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				conHolder.setConnection(fetchConnection(dataSource));
			}
			return conHolder.getConnection();
		}
		// 通过 DataSource 创建新的Connection
		Connection con = fetchConnection(dataSource);
		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			ConnectionHolder holderToUse = conHolder;
			if (holderToUse == null) {// 创建 ConnectionHolder
				holderToUse = new ConnectionHolder(con);
			}else {
				holderToUse.setConnection(con);
			}
			holderToUse.requested();
			// 初始化TransactionSynchronization之ConnectionSynchronization
			TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));
			holderToUse.setSynchronizedWithTransaction(true);
			if (holderToUse != conHolder) {
				// 将 Connection 与当前线程建立绑定关系
				TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
			}
		}
		return con;
	}
}

注意:通过DataSourceUtils获取数据库连接只发生在Mybatis执行目标SQL之前,并且只有此处重置ConnectionSynchronization。


 2.SqlSession

通过SqlSessionFactory工厂类得到的SqlSession与线程绑定的流程:

  1. 首先Synchronization处于激活状态。这个激活动作是被事务触发,所以如果目标方法不存在事务则SqlSession不会与线程绑定。
  2. 初始化SqlSession的Holder之SqlSessionHolder。
  3. 将SqlSession与线程建立绑定关系。
  4. 初始化SqlSessionSynchronization实例,并与线程建立绑定关系。
  5. 设置SqlSessionHolder之synchronizedWithTransaction属性为true。
public final class SqlSessionUtils {

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {
        // 尝试由 事务同步管理器 获取SqlSession
        SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
        // 如果 holder 不为空,则尝试从 holder 直接获取SqlSession
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
          return session;
        }
        // 通过 SqlSessionFactory工厂类 创建新的 SqlSession
        session = sessionFactory.openSession(executorType);
        // 尝试将 新得到的SqlSession 与 事务同步管理器 建立绑定关系
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
    }

    private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
        SqlSession session = null;
        // 如果 holder 不为空,并且 SqlSession 是跟事务相关的
        if (holder != null && holder.isSynchronizedWithTransaction()) {
          holder.requested();
          session = holder.getSqlSession();
        }
        return session;
    }

    private static void registerSessionHolder(SqlSessionFactory sf, ExecutorType ct,PersistenceExceptionTranslator et, SqlSession session) {
      SqlSessionHolder holder;
      // 首先同步器必须处于激活状态
      if (TransactionSynchronizationManager.isSynchronizationActive()) {
        Environment environment = sf.getConfiguration().getEnvironment();
        // 必须是事务管理器,好像默认都是 SpringManagedTransactionFactory
        if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
          holder = new SqlSessionHolder(session, ct, et);
          // 真正建立 资源与线程 的绑定关系
          TransactionSynchronizationManager.bindResource(sf, holder);
          TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sf));
          // 设置 SqlSessionHolder 的属性SynchronizedWithTransaction 为true
          holder.setSynchronizedWithTransaction(true);
          holder.requested();
        } 
      } 
    }
}

所以同步事务管理器如果对SqlSession发挥作用则必定存在于事务下。

3.ResourceHolder

public abstract class ResourceHolderSupport{

	private boolean synchronizedWithTransaction = false;
}

 如上所示ResourceHolder存在一个重要属性:synchronizedWithTransaction。

3.1.ConnectionHolder

public class ConnectionHolder extends ResourceHolderSupport {

	@Nullable
	private ConnectionHandle connectionHandle;

	@Nullable
	private Connection currentConnection;

	private boolean transactionActive = false;

}

数据库连接Holder:分别持有Connection以及transactionActive属性。

3.2.SqlSessionHolder

public final class SqlSessionHolder extends ResourceHolderSupport {

  private final SqlSession sqlSession;

  private final ExecutorType executorType;

  private final PersistenceExceptionTranslator exceptionTranslator;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+Redis实现接口限流

前言 业务中需要对一些接口进行限流处理&#xff0c;防止机器人调用或者保证服务质量&#xff1b; 实现方式 基于redis的lua脚本 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis&…

Linux Deepin系统安装x11vnc+cpolar实现Windows系统电脑远程其桌面

文章目录 1. 安装x11vnc2. 本地远程连接测试3. Deepin安装Cpolar4. 配置公网远程地址5. 公网远程连接Deepin桌面6. 固定连接公网地址7. 固定公网地址连接测试 x11vnc是一种在Linux系统中实现远程桌面控制的工具&#xff0c;它的原理是通过X Window系统的协议来实现远程桌面的展…

linux用git拉取我云端以及git处理冲突

拉取后切换一个跟云端分支(dev)一样的 git branch --set-upstream-toorigin/dev dev 之后就同步了 A在dev分支写了iii,提交 B在dev分支写了hhh,提交,冲突 怎么修改,B把云端的拉下来,随便改改就行

YOLOv7 | 添加GSConv,VoVGSCSP等多种卷积,有效提升目标检测效果,代码改进(超详细)

⭐欢迎大家订阅我的专栏一起学习⭐ &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; YOLOv5涨点专栏&#xff1a;http://t.csdnimg.cn/QdCj6 YOLOv7专栏&#xff1a; http://t.csdnimg.cn/dy…

Qt 多元素控件

Qt开发 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别 以 QTableWidget 和 QTableView 为例. QTableView 是基于 MVC 设计的控件. QTableView 自身不持有数据. 使用QTableView 的 …

latex如何让标题section取消数字标号

解决方法——加一个*号 在LaTeX中&#xff0c;如果你想让section标题取消数字标号&#xff0c;可以使用section*代替section。section*将生成一个不带数字标号的节标题。 例如&#xff0c;你可以这样写&#xff1a; \section*{这是不带数字标号的节标题}这将生成一个标题&am…

给老婆整了个短剧搜索机器人APP

最近短剧挺火&#xff0c;很多群友们都在做一些资源分享&#xff0c;老胡于是基于这些资源做了个短剧搜索引擎&#xff0c;挺多朋友喜欢看的&#xff0c;我老婆也在看哈哈&#xff0c;真上头&#xff0c;废话不多说&#xff0c;上短剧机器人。 短剧机器人 直接在微信群输入&…

解决由于历史原因解析tflite失败的问题

文章目录 0. 背景1. tflite 历史遗留问题2. schema3. flatbuffers 编译器3.1 安装 FlatBuffers 编译器3.2. 编译 FlatBuffers schema 文件3.3 使用生成的 Python 文件 4 问题未解决终极解决方案 写在最前面&#xff1a;解决方法是升级tensorflow版本&#xff0c;重新生成tflite…

【go从入门到精通】if else 条件控制

Go 语言条件语句&#xff1a; 在 Go 语言中&#xff0c;条件语句用于根据不同的条件执行不同的代码。Go 语言提供了两种条件语句&#xff1a;if 语句和switch 语句。 if语句 if由一个布尔表达式后紧跟一个或多个语句组成。 语法 Go 编程语言中 if 语句的语法如下&#xff…

反向海淘系统中的数据安全挑战与解决方案探讨

**反向海淘系统中的数据安全挑战与解决方案探讨** **一、背景** 随着反向海淘市场的不断扩大&#xff0c;涉及的数据安全挑战也日益增多。本文旨在探讨反向海淘系统中面临的数据安全挑战&#xff0c;以及相应的解决方案。 **二、数据安全挑战** 1. **数据传输安全**&#x…

微信支付宝--充ChatGPTPLUS/openAI key

ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够基于在预训练阶段所见的模式和统计规律&#xff0c;来生成回答&#xff0c;还能根据聊天的上下文进行互动&#xff0c;真正像人类一样来聊天交流&#xff0c;甚至能完成撰写论文、邮件、脚本、文案、翻译、代码等…

【Python】反编译PyInstaller打包的exe

查看exe基本信息 需要反编译的exe 查看exe文件的打包工具&#xff0c;查看exe信息的软件叫Detect It Easy(查壳工具) 由图我们可以看出当前选中的exe文件是由名叫PyInstaller的打包工具打包好的exe 反编译 exe反编译工具&#xff1a;pyinstxtractor.py 使用方法 python py…

面试算法-65-二叉树的层平均值

题目 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[3.00000,14.50000,11.00000] 解释&#xff1a;第 0 层的…

【Spring高级】AOP和动态代理

目录 AspectJ实现AOPJava Agent实现AOPProxy&#xff08;代理&#xff09;模式实现AOPJDK代理CGLIB代理 AOP的底层实现切点Aspect与Advisor切面AOP底层的实现演示 Spring中的代理选择 在Java中&#xff0c;AOP&#xff08;面向切面编程&#xff09;的实现可以通过以下几种方法&…

MySQL的日志:undo log、redo log、binlog有什么作用

目录 从一个update语句说起 undo log 为什么需要undo log undo log 版本链 undo log 是如何持久化到磁盘? redo log 为什么需要redo log redo的组成 redo Log的刷盘策略 redo Log循环写 crash-safe能力 binlog 为什么需要 binlog &#xff1f; binlog与redo lo…

淘宝API接口开发系列——淘宝详情数据采集

淘宝详情数据采集涉及多种技术和方法&#xff0c;下面列举几种常见的方式&#xff1a; 请求示例&#xff0c;API接口接入Anzexi58 爬虫技术&#xff1a;使用编程语言&#xff08;如Python&#xff09;编写网络爬虫程序&#xff0c;通过模拟浏览器行为访问淘宝网站&#xff0c;…

XMind:让思维可视化,提升工作效率的利器

XMind是一款全球领先的开源思维导图和头脑风暴软件&#xff0c;它应用全球最先进的Eclipse RCP软件架构&#xff0c;拥有优秀的用户体验&#xff0c;凭借简单易用、功能强大的特点&#xff0c;在2013年被著名互联网媒体Lifehacker评选为全球最受欢迎的思维导图软件。目前&#…

PyQt上手指南

文章目录 前言PyQt的好处从一个最简单的例子入手PyQt5基础组件体系源码结构 Qt Designer基础布局高级界面Web控件 多线程列表图形绘制PyQt5.QtGuiPyQtGraphmatplotlib和PyQt结合和mplfinance结合 工具使用打包链接 前言 用户界面开发&#xff0c;我搞过visual C MFC、Delphi V…

【链表】Leetcode 142. 环形链表 II【中等】

环形链表 II 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系…

ruoyi-activiti添加用车申请流程(二)

实体类Car中必须要有String userId属性。 设置自定义表单为system/car/deptleadercheck&#xff1a; 然后在CarController中编写system/car/deptleadercheck对应的函数&#xff1a; GetMapping("/deptleadercheck")public String deptleadercheck(String taskid, M…