【Spring框架】Spring事务同步

目录

一、什么是Spring事务同步

二、 事务同步管理器

2.1 TransactionSynchronizationManager事务同步管理器

2.1.1 资源同步

2.1.2 事务同步

2.1.3 总结

三、事务同步管理器保障事务的原理

四、spring事务为何使用TransactionSynchronizationManager

spring源码实现

五、TransactionSynchronization--(before/after-commit/comple)


一、什么是Spring事务同步

Spring 将 JDBC 的 Connection、Hibernate 的 Session 等访问数据库的连接或者会话对象统称为资源,这些资源在同一时刻是不能多线程共享的 。 为了让 DAO 或 Service 类可以实现单例模式,Spring的事务同步管理器org.springframework.transaction.support.TransactionSynchronizationManager类利用 ThreadLocal 为不同的事务线程提供了独立的资源副本,并同时维护这些事务的配置属性和运行状态信息 。它提供了一些静态方法来操作和获取线程绑定的资源,如bindResource、getResource、unbindResource等。

Spring的事务同步还涉及到事务传播行为,即在一个事务方法中调用另一个事务方法时,后者的事务如何与前者的事务关联。

总的来说Spring事务同步的作用就是在不同的事务线程中保证资源的一致性和事务的正确性。Spring事务同步的主要功能有:

  • 通过ThreadLocal为每个事务线程提供独立的资源副本,如数据库连接、会话对象等,避免线程安全问题。
  • 通过TransactionSynchronizationManager类管理和操作线程绑定的资源,如绑定、获取、解绑等。
  • 通过事务传播行为控制不同事务方法之间的事务关联,如是否使用同一个事务、是否创建新的事务等。
  • 通过事务同步器(TransactionSynchronization)实现事务的扩展功能,如在事务提交前后执行一些额外的操作。

二、 事务同步管理器

2.1 TransactionSynchronizationManager事务同步管理器

TransactionSynchronizationManager事务同步管理器,管理每个线程的资源(对于事务,DataSource创建的连接对象connection等称作事务的资源)和事务同步(TransactionSynchronization---用来监听事务操作的回调类,其中定义了在事务执行过程中,进行的拓展操作,如before/after--commit/completion,在getSynchronizations中,对其进行sort排序返回)。

同步分两种,资源的同步和事务的同步。

2.1.1 资源同步

此处就是数据库DataSource的连接connection,保证在一个线程中的事务操作,能够获取同一个connection资源。因此资源存入ThreadLocal<Map<Object, Object>> resources中,保证线程之间的事务操作的隔离(因为获取不同connection,由数据库事务实现spring事务)

2.1.2 事务同步

也就是事务方法同步,synchronizations内的TransactionSynchronization对象的集合,其用来在事务提交前、后,事务完成前后进行的实际操作,其事务操作在各个阶段的执行流程在AbstractPlatformTransactionManager中定义。

2.1.3 总结

TransactionSynchronizationManager 通过 ThreadLocal 对象在当前线程记录了 resources 和 synchronizations属性。resources 是一个 HashMap,用于记录当前参与事务的事务资源,方便进行事务同步在 DataSourceTransactionManager 的例子中就是以 dataSource 作为 key,保存了数据库连接,这样在同一个线程中,不同的方法调用就可以通过 dataSource 获取相同的数据库连接,从而保证所有操作在一个事务中进行。synchronizations 属性是一个 TransactionSynchronization对象的集合,AbstractPlatformTransactionManager 类中定义了事务操作各个阶段的调用流程

三、事务同步管理器保障事务的原理

spring的事务通过数据库DataSource获取connection来实现,为了使事务方法service.methodA,调用dao.methodB时,仍然能够位于当前事务,如此,能够使得service和dao的调用,在同线程的情况下,都可以获取到相同的connection,就保证了两个操作都在同一个事务。所以需要将connection共享,并考虑使用线程共享变量,threadLocal<Map(datasource,connection)>保存共享的connection。

public abstract class TransactionSynchronizationManager {/**…………………………………………………………………………………………………………同步资源……………………………………………………………………………………………… */// resource相当于一个(threadID,map(datasource,connectionHolder))的属性,这里用ThreadLocal保存// key为DataSource,value为connectionHolder(保存当前threadID的connection)// 也就是存储的当前线程ID中不同的数据源DataSource对应的connection,这样能保证在同一个事务线程中,获取相同数据源DataSource的connection都是同一个connectionprivate static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/**………………………………………………………………………TransactionSynchronization管理…………………………………………………………………………………………… */// 当前线程所需要的事务同步器TransactionSynchronization集合// TransactionSynchronization用来在事务的执行阶段前后,进行回调操作,如before/after-commit/completionprivate static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");// 事务同步管理器一起维护这些事务属性,保证事务属性的一致性和正确性// 当前事务名称private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<>("Current transaction name");private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<>("Current transaction read-only status");// 当前事务的隔离级别private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<>("Current transaction isolation level");// 当前事务是否activeprivate static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<>("Actual transaction active");// 获取当前是否存在事务(判断线程共享变量,是否存在TransactionSynchronizationpublic static boolean isSynchronizationActive() {return (synchronizations.get() != null);}/**……………………………………………………………………获取connection资源……………………………………………………………………………… */@Nullablepublic static Object getResource(Object key) {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);return doGetResource(actualKey);}@Nullableprivate static Object doGetResource(Object actualKey) {Map<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.get(actualKey);// Transparently remove ResourceHolder that was marked as void(无效的)...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {resources.remove();}value = null;}return value;}//DataSourceTransactionManager.doBegin方法中,将新创建的connection包装成connectionHolder,并存入ThreadLocal<Map<Object, Object>> resourcespublic static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");Map<Object, Object> map = resources.get();// set ThreadLocal Map if none foundif (map == null) {map = new HashMap<>();resources.set(map);}Object oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");}}/**……………………………………………………………………获取TransactionSynchronization资源……………………………………………………………………………… */public static List<TransactionSynchronization> getSynchronizations() throws IllegalStateException {Set<TransactionSynchronization> synchs = synchronizations.get();if (synchs == null) {throw new IllegalStateException("Transaction synchronization is not active");}// Return unmodifiable snapshot, to avoid ConcurrentModificationExceptions// while iterating and invoking synchronization callbacks that in turn// might register further synchronizations.if (synchs.isEmpty()) {return Collections.emptyList();}else {// Sort lazily here, not in registerSynchronization.List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);//TransactionSynchronization进行排序OrderComparator.sort(sortedSynchs);return Collections.unmodifiableList(sortedSynchs);}}
}

四、spring事务为何使用TransactionSynchronizationManager

一个web项目的主要逻辑模块如下:

spring的事务,通常是在service层的某个方法做完整的事务。service要完成事务,需要如下点:

  1. service在系统中是个单例对象,且service需要通过持有DataSource.connection连接对象,通过数据库事务来实现spring事务;
  2. 一个DataSource可以创建多个connection,每个conn需要被一个线程在执行service时候持有,才能在当前调用中完整的通过获取同一个conn实现事务的begin、commit、rollback;
  3. service调用,需要与DAO层交互,因此也要保证DAO也可以获取同一个conn;
  4. Datasource应该单独放在一个类中,以便对于不同的用户线程执行service事务时DataSource.getConnection获取连接。

因此,考虑如上几点需求,需要将不同线程对应的conn存放在ThreadLocal中,能够保证事务执行中,同个线程都可以跨模块获取该conn,保证处于同一事务中。

spring中通过事务同步管理器TransactionSynchronizationManager实现上述推论。

public abstract class TransactionSynchronizationManager {/**…………………………………………………………………………………………………………同步资源……………………………………………………………………………………………… *///resource相当于一个(threadID,map(datasource,connectionHolder))的属性,这里用ThreadLocal保存//key为DataSource,value为connectionHolder(保存当前threadID的connection)private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");...
}

spring事务中DataSource,通过DataSourceTransactionManager保存。

spring源码实现

事务开启,通过transactionManager.getTransaction。

// AbstractPlatformTransactionManager.java
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());// 获取事务对象DataSourceTransactionObjectObject transaction = doGetTransaction();...
}
// DataSourceTransactionManager.java
protected Object doGetTransaction() {DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());ConnectionHolder conHolder =//TransactionSynchronizationManager通过DataSource创建connection,包装成ConnectionHolder(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());//设置conHolder入事务对象DataSourceTransactionObjecttxObject.setConnectionHolder(conHolder, false);return txObject;
}public abstract class TransactionSynchronizationManager {private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");public static Object getResource(Object key) {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Object value = doGetResource(actualKey);return value;}private static Object doGetResource(Object actualKey) {//从ThreadLocal<Map<Object, Object>> resources中获取当前线程对应的事务连接connectionMap<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.get(actualKey);return value;}
}

五、TransactionSynchronization--(before/after-commit/comple)

事务同步器,我们可以自定义实现TransactionSynchronization类,来监听Spring的事务操作,通过TransactionSynchronization类的这些回调方法做一些扩展。

public interface TransactionSynchronization extends Flushable {//描述事务当前状态int STATUS_COMMITTED = 0;int STATUS_ROLLED_BACK = 1;int STATUS_UNKNOWN = 2;//挂起该事务同步器default void suspend() {}//恢复事务同步器default void resume() {}//flush 底层的session到数据库@Overridedefault void flush() {}//事务提交前的回调default void beforeCommit(boolean readOnly) {}//事务commit/rollback前的回调,用于在事务完成前进行资源清除default void beforeCompletion() {}//事务提交后的回调,可以用于在事务成功提交后做的进一步的操作,例如:在数据提交到数据库中后,发送确认的短信或邮件default void afterCommit() {}//在事务commit/rollback之后进行回调,例如可以在事务完成后做一些资源清除default void afterCompletion(int status) {}
}

举例:

TransactionSynchronization是注册在TransactionSynchronizationManager内,需要其内的方法判断是否存在事务,是否可以执行事务同步方法。

// 当前事务提交后方可进行异步任务,防止异步任务先于未提交的事务执行
private void callBack(Invoice invoice){boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();// 当前存在事务,在事务提交后执行  if (synchronizationActive) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() { // 监听事务提交完成// 事务提交后执行异步任务doCall(invoice);}});} else {// 当前不存在事务,直接执行doCall(invoice);}
}

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

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

相关文章

第二十七天| 39. 组合总和 、40.组合总和II、131.分割回文串

Leetcode 39. 组合总和 题目链接&#xff1a;39 组合总和 题干&#xff1a;给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序…

【网络】:序列化和反序列化

序列化和反序列化 一.json库 二.简单使用json库 前面已经讲过TCP和UDP&#xff0c;也写过代码能够进行双方的通信了&#xff0c;那么有没有可能这种通信是不安全的呢&#xff1f;如果直接通信&#xff0c;可能会被底层捕捉&#xff1b;可能由于网络问题&#xff0c;一方只接收到…

JavaScript中的for循环和map方法

JavaScript中的for循环和map方法 在JavaScript中&#xff0c;循环是一种常见的编程技巧&#xff0c;用于重复执行一段代码。for循环和map方法都可以用于循环操作&#xff0c;但它们在语法和应用场景上存在一些区别。本文将详细讲解JavaScript中的for循环和map方法&#xff0c;以…

【Linux技术宝典】Linux入门:揭开Linux的神秘面纱

文章目录 官网Linux 环境的搭建方式一、什么是Linux&#xff1f;二、Linux的起源与发展三、Linux的核心组件四、Linux企业应用现状五、Linux的发行版本六、为什么选择Linux&#xff1f;七、总结 Linux&#xff0c;一个在全球范围内广泛应用的开源操作系统&#xff0c;近年来越来…

神经网络(Nature Network)

最近接触目标检测较多&#xff0c;再此对最基本的神经网络知识进行补充&#xff0c;本博客适合想入门人工智能、其含有线性代数及高等数学基础的人群观看 1.构成 由输入层、隐藏层、输出层、激活函数、损失函数组成。 输入层&#xff1a;接收原始数据隐藏层&#xff1a;进行…

Stable Diffusion 模型下载:Disney Pixar Cartoon Type B(迪士尼皮克斯动画片B类)

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十

阿里云服务器带宽计费模式是什么?怎么选择?

阿里云服务器带宽计费模式分为“按固定带宽”和“按使用流量”&#xff0c;有什么区别&#xff1f;按固定带宽是指直接购买多少M带宽&#xff0c;比如1M、5M、10M、100M等&#xff0c;阿里云直接分配用户所购买的带宽值&#xff0c;根据带宽大小先付费再使用&#xff1b;按使用…

Windows Server 2019 搭建并加入域

系列文章目录 目录 系列文章目录 文章目录 前言 一、域是什么&#xff1f; 二、配置服务器 1.实验环境搭建 1)实验服务器配置和客户端 2)实验环境 2.服务器配置 账户是域服务器的账户和密码 文章目录 Windows Server 2003 Web服务器搭建Windows Server 2003 FTP服务器搭…

Hadoop:认识MapReduce

MapReduce是一个用于处理大数据集的编程模型和算法框架。其优势在于能够处理大量的数据&#xff0c;通过并行化来加速计算过程。它适用于那些可以分解为多个独立子任务的计算密集型作业&#xff0c;如文本处理、数据分析和大规模数据集的聚合等。然而&#xff0c;MapReduce也有…

《UE5_C++多人TPS完整教程》学习笔记4 ——《P5 局域网连接(LAN Connection)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P5 局域网连接&#xff08;LAN Connection&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;也是译者&…

Java 学习和实践笔记(6)

各数据类型所占的空间&#xff1a; byte: 1个字节 short&#xff1a;2个字节 int&#xff1a;4个 long&#xff1a;8个 float&#xff1a;4个 double: 8个 char:1个 boolean:1bit 所有引用数据类型都是4个字节&#xff0c;实际其值是指向该数据类型的地址。 上图中稍特…

blender怎么保存窗口布局,怎么设置默认输出文件夹

进行窗口布局大家都会&#xff0c;按照自己喜好来就行了&#xff0c;设置输出文件夹如图 这些其实都简单。关键问题在于&#xff0c;自己调好了窗口布局&#xff0c;或者设置好了输出文件夹之后&#xff0c;怎么能让blender下次启动的时候呈现出自己设置好的窗口布局&#xff…

【开源】SpringBoot框架开发木马文件检测系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 木马分类模块2.3 木马软件模块2.4 安全资讯模块2.5 脆弱点模块2.6 软件检测模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 木马分类表3.2.2 木马软件表3.2.3 资讯表3.2.4 脆弱点表3.2.5 软件检测表…

有人说可视化大屏是讨好领导的,有错么?难道讨好你这个大头兵

最近我分享了一批大数据可视化的界面&#xff0c;大部分粉丝都是认可的&#xff0c;也有粉丝想不到这个有啥用&#xff0c;极个别人非常酸&#xff0c;认为这个除了讨好领导&#xff0c;屁用没有。 客户既然花大钱找我们&#xff0c;肯定有用处。 首先&#xff0c;这里我给解…

Days28 ElfBoard 板]修改开机动画

1.可能需要安装的库 elfubuntu:~/work/psplash$ sudo apt-get install build-essential libncurses5-dev elfubuntu:~/work/psplash$ sudo apt-get install libtool elfubuntu:~/work/psplash$ sudo apt-get install gettext elfubuntu:~/work/psplash$ sudo apt-get install l…

Unity学习笔记(零基础到就业)|Chapter04:C#篇补充到Unity篇过渡

Unity学习笔记&#xff08;零基础到就业&#xff09;&#xff5c;Chapter02:C#篇补充到Unity篇过渡 前言C#总结补充1.值类型和引用类型有什么区别&#xff0c;他们在值的传递上分别有怎样的特性2.string是引用类型&#xff0c;但是他对外表现出值类型的特性&#xff0c;为什么&…

【AI大模型应用开发】【LangChain系列】6. LangChain的Callbacks模块:监控调试程序的重要手段

大家好&#xff0c;我是【同学小张】。持续学习&#xff0c;持续干货输出&#xff0c;关注我&#xff0c;跟我一起学AI大模型技能。 LangChain提供了一个回调系统&#xff0c;允许您挂接到LLM应用程序的各个阶段。这对于日志记录、监视、流式传输和其他任务非常有用。 0. Lang…

【快速上手QT】02-学会查看QT自带的手册QT助手

QT助手 为什么大家都说QT简单&#xff0c;第一点就是确实简单&#xff08;bushi&#xff09;。 我个人觉得最关键的点就是人家QT官方就给你准备好了文档&#xff0c;甚至还有专门的IDE——QtCreator&#xff0c;在QTCreator里面还有很多示例代码&#xff0c;只要你会C的语法以…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Divider组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Divider组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Divider组件 提供分隔器组件&#xff0c;分隔不同内容块/内容元素。 子组件 …

C#使用重载方法实现不同类型数据的计算

目录 一、涉及到的相关知识 1.重载的方法 2.Convert.ToInt32(String)方法 3.判断字符串是否带有小数点 二、实例 1.示例 2.生成成果 一、涉及到的相关知识 1.重载的方法 重载方法就是方法名称相同&#xff0c;但是每个方法中参数的数据类型、个数或顺序不同的方法。如果…