事务同步管理器TransactionSynchronizationManager

news2024/12/27 12:26:45

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

  • 同步涉及的资源包括: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;
}

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

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

相关文章

Swift 从获取所有 NSObject 对象聊起:ObjC、汇编语言以及底层方法调用链(一)

概览 Swift 语言给我们的印象是&#xff1a;简洁、现代化和可以“心安神泰”的完全信赖。不过&#xff0c;在一些特殊情况下我们唯有进入 Swift 底层的动态世界方能真正地“随遇而安”。 保安局“刘局长”曾语重心长的教导过我们&#xff1a;“非常时期&#xff0c;用非常方法…

MATLAB环境下基于机器学习的合成数据生成方法

合成数据是通过计算机程序人工生成的数据&#xff0c;而不是由真实事件生成的数据。采用合成数据来增加训练数据&#xff0c;可以节省数据采集费用&#xff0c;或满足隐私要求。随着计算能力的提高和云数据存储选项的崛起&#xff0c;合成数据比以往更容易获取。这无疑是一个积…

VO、RVO、ORCA(动态避障)算法

碰撞锥&#xff08;碰撞区域&#xff09; 上上图中假设B物体处于静止状态&#xff0c;A物体沿着向量v1和v2移动&#xff0c;刚好能和B擦肩而过&#xff0c;不会发生碰撞&#xff1b;若V1和V2的夹角再小一点的话就一定会发生碰撞。此时会产生碰撞区域&#xff1a; 红线画出来…

学生信息管理系统--修改信息(非常详细的修改,更新,撤销,删除逻辑)

目录 概述修改包括的操作修改在每个模块中的应用 详解修改与更新取消删除 特殊概念数据集游标 总结 概述 学生信息管理系统&#xff0c;功能相对简单且代码重复性高&#xff0c;应该采用复用的思想来减少代码的冗余和提高代码的可维护性。然而&#xff0c;对于基础入门项目来说…

【Web APIs】DOM获取元素

目录 1.Web API基本认识 2.获取DOM元素 3.设置/修改DOM元素内容 4.设置/修改DOM元素属性 4.1修改元素常用属性 4.2修改元素样式属性 4.3设置/修改表单属性 5.定时器-间歇函数 1.Web API基本认识 作用&#xff1a;就是使用js去操作html和浏览器 分类&#xff1a;DOM&am…

一网打尽音乐制作!FL Studio21.2.3中文版全能数字音频工作站

一网打尽音乐制作&#xff01;FL Studio21中文版全能数字音频工作站&#xff0c;专业机构力荐 &#xff1a;打造专业音乐制作&#xff0c;让音乐梦想触手可及&#xff01; FL Studio是一款广泛使用的数字音频工作站&#xff0c;被许多音乐制作人和DJ用来制作和混合音乐。最新版…

基于torch.compile和gptfast代码风格实现ChatGLM模型推理加速

目录 一、ChatGLM模型代码重构迁移 二、推理的代码重构 三、效果分析对比 参考文章 torch2.0发布以后模型训练和推理可以实现一行代码加速&#xff0c;试用之后发现效果并不明显。随后gptfast项目也发布&#xff0c;表明它确实是可以实现模型推理的加速&#xff0c;看来之前…

修改约束

目录 修改约束 创建数据库 添加约束 删除约束 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 修改约束 如果说表结构的修改还在可以容忍的范畴之内&#xff0c;那么约束的修改是绝对 100% 禁止的 所有的约束一定要在…

微信小程序项目实战遇到的问题

我们以学生成绩平台来作为例子。这是我们想得到的效果。 以下是完整代码&#xff1a; index.js // index.js Page({//页面的初始数据data: {hello: 欢迎进入微信小程序的编程世界,score: 80,userArray: [{name: 张三,score: [66, 77, 86, 70, 90]},{name: 李四,score: [88, 7…

jwt以及加密完善博客系统

目录 一、背景 二、传统登陆功能&强制登陆功能 1、传统的实现方式 2、session存在的问题 三、jwt--令牌技术 1、实现过程 2、令牌内容 3、生成令牌 4、检验令牌 四、JWT登陆功能&强制登陆功能 1、JWT实现登陆功能 2、强制登陆功能 3、运行效果 五、加密/加…

全栈的自我修养 ———— 微信小程序开发电脑测试api请求正常,移动端请求异常!!

小编今天也是在电脑测试时候发送请求http到服务器是可以通的&#xff0c;但是到了手机端就不可以了&#xff0c;经过小编仔细钻研&#xff0c;终于发现了以下问题&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

Java面试题(Spring篇)

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 Java面试题(Spring篇) 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收藏⭐ 评论&#x1f4c4; 小王的主页…

Hive SQL必刷练习题:日期交叉问题(两种思路)

思路一&#xff1a; ​ 首先想到的是借助炸裂函数&#xff0c;一行变成多行&#xff0c;就可以进行去重操作&#xff0c;然后再统计日期。 用到炸裂函数&#xff0c;就首先需要可以拿到起始和终止日期差大小的数组&#xff0c;然后再炸裂​ 那这个指定长度数组怎么获取呢&…

STM32初识3

中断和事件 什么是中断&#xff1f; 中断是指计算机运行过程中&#xff0c;出现某些意外情况需主机干预时&#xff0c;机器能自动停止正在运行的 程序并转入处理新情况的程序&#xff0c;处理完毕后又返回原被暂停的程序继续运行。 什么是EXTI&#xff1f; …

外包干了1个月,技术明显进步。。。

我是一名大专生&#xff0c;自19年通过校招进入湖南某软件公司以来&#xff0c;便扎根于功能测试岗位&#xff0c;一晃便是近四年的光阴。今年8月&#xff0c;我如梦初醒&#xff0c;意识到长时间待在舒适的环境中&#xff0c;已让我变得不思进取&#xff0c;技术停滞不前。更令…

Docker实现在线代码运行平台-源码分析

一、背景 也许大家平时会有用到一些在线代码运行的网址, 方便我们做一些语法测试或者学习。 例如执行Python代码、Java代码、Shell代码等等, 有时候自己本地环境不具备的时候做一些简单的脚本测试还是蛮实用和方便的。 例如这个在线运行的代码站点: https://r.xjq.icu/ 但是你…

ORB-SLAM2安装和运行教程

前言 默认已经安装了cmake、git 、gcc 和g 更新apt库,更新软件列表 sudo apt-get update版本 以下库均可以通过apt-get方式安装&#xff0c;但是非常不建议这样做&#xff0c;版本不同&#xff0c;最后会报很多错&#xff0c;强烈建议源码安装&#xff01;&#xff01;&…

LeetCode 17 / 100

目录 普通数组最大子数组和合并区间轮转数组除自身以外数组的乘积缺失的第一个正数 LeetCode 53. 最大子数组和 LeetCode 56. 合并区间 LeetCode 189. 轮转数组 LeetCode 238. 除自身以外数组的乘积 LeetCode 41. 缺失的第一个正数 普通数组 最大子数组和 给你一个整数数组 …

练习 9 Web [SUCTF 2019]CheckIn (未拿到flag)

上传图片格式的木马文件&#xff1a; 返回 <? in contents!,存在PHP代码检测 上传非图片格式文件&#xff1a; 返回 不允许非image 修改木马PHP代码规避检测 <? ?> 改为 < script language“php”>< /script ><?php eval($_POST[shell]);?>…

多模态大语言模型的 (R) 演变:调查

目录 1. Introduction2. 赋予LLMs多模态能力2.1 大型语言模型2.2 视觉编码器2.3 视觉到语言适配器2.4 多模式训练 3. 使用 MLLM 处理视觉任务 连接文本和视觉模式在生成智能中起着至关重要的作用。因此&#xff0c;受大型语言模型成功的启发&#xff0c;大量研究工作致力于多模…