【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.rhkb.cn/news/254912.html

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

相关文章

【Linux】Shell编程

Shell编程 目录 Shell编程1.shell基础1.输入重定向 & 输出重定向2.管道3.特殊字符(3.1)通配符(3.2)引号(3.3)注释符(#) 4.别名5.命令历史history 2.Shell脚本Shell脚本的执行方式(1)为脚本文件加上可执行权限,然后在命令行直接输入shell脚本文件名执行。(2)sh shell脚本名(…

LabVIEW与EtherCAT实现风洞安全联锁及状态监测

LabVIEW与EtherCAT实现风洞安全联锁及状态监测 在现代风洞试验中&#xff0c;安全联锁与状态监测系统发挥着至关重要的作用&#xff0c;确保了试验过程的安全性与高效性。介绍了一套基于EtherCAT总线技术和LabVIEW软件开发的风洞安全联锁及状态监测系统。该系统通过实时、可靠…

SegmentAnything官网demo使用vue+python实现

一、效果&准备工作 1.效果 没啥好说的&#xff0c;低质量复刻SAM官网 https://segment-anything.com/ 需要提一点&#xff1a;所有生成embedding和mask的操作都是python后端做的&#xff0c;计算mask不是onnxruntime-web实现的&#xff0c;前端只负责了把rle编码的mask解…

Web Services 服务 是不是过时了?创建 Web Services 服务实例

Web Services 是不是过时了&#xff1f; 今天是兔年最后一天&#xff0c;先给大家拜个早年 。 昨天上午视频面试一家公司需要开发Web Services 服务&#xff0c;这个也没有什么&#xff0c;但还需要用 VB.net 开发。这个是多古老的语言了&#xff0c;让我想起来了 10年 前 写 …

Eclipse安装配置、卸载教程(Windows版)

Eclipse是一个开放源代码的集成开发环境&#xff08;IDE&#xff09;&#xff0c;最初由IBM公司开发&#xff0c;现在由Eclipse基金会负责维护。它是一个跨平台的工具&#xff0c;可以用于开发多种编程语言&#xff0c;如Java、C/C、Python、PHP、Rust等。 Eclipse提供了一个可…

Android AOSP源码研究之万事开头难----经验教训记录

文章目录 1.概述2.Android源下载1.配置环境变量2.安装curl3.下载repo并授权4.创建一个文件夹保存源码5.设置repo的地址并配置为清华源6.初始化仓库7.指定我们需要下载的源码分支并初始化 2.1 使用移动硬盘存放Android源码的坑2.2 解决方法 3.Android源码编译4.Android源烧录 1.…

MySQL篇之回表查询

一、聚集索引 将数据存储与索引放到了一块&#xff0c;索引结构的叶子节点保存了行数据。特点&#xff1a;必须有,而且只有一个。 聚集索引选取规则: 1. 如果存在主键&#xff0c;主键索引就是聚集索引。 2. 如果不存在主键&#xff0c;将使用第一个唯一&#xff08;UNIQUE&am…

【开源】基于JAVA+Vue+SpringBoot的智慧社区业务综合平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 业务类型模块2.2 基础业务模块2.3 预约业务模块2.4 反馈管理模块2.5 社区新闻模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 业务类型表3.2.2 基础业务表3.2.3 预约业务表3.2.4 反馈表3.2.5 社区新闻表 四、系统展…

深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之行存(一)

在当今的大数据时代&#xff0c;高效的数据检索和分析能力已成为许多应用程序的核心需求。Elasticsearch&#xff0c;作为一款强大的分布式搜索和分析引擎&#xff0c;正是为了满足这些需求而诞生的。它之所以能够在海量数据中实现毫秒级的搜索响应&#xff0c;以及灵活的数据分…

Docker进阶篇-CIG重量级监控系统

一、简介 通过docker stats命令可以很方便的查看当前宿主机上所有容器的CPU、内存、网络流量等数 据&#xff0c;可以满足一些小型应用。 但是docker stats统计结果只能是当前宿主机的全部容器&#xff0c;数据资料是实时的&#xff0c;没有地方存储、 没有健康指标过线预警…

肯尼斯·里科《C和指针》第13章 高级指针话题(3)命令行参数

处理命令行参数是指向指针的指针的另一个用武之地。有些操作系统&#xff0c;包括UNIX和MS-DOS&#xff0c;让用户在命令行中编写参数来启动一个程序的执行。这些参数被传递给程序&#xff0c;程序按照它认为合适的任何方式对它们进行处理。 13.4.1 传递命令行参数 这些参数如何…

html5 audio video

DOMException: play() failed because the user didn‘t interact with the document first.-CSDN博客 不可用&#xff1a; 可用&#xff1a; Google Chrome Close AutoUpdate-CSDN博客

用HTML5实现灯笼效果

本文介绍了两种实现效果&#xff1a;一种使用画布&#xff08;canvas&#xff09;标签/元素&#xff0c;另一种不用画布&#xff08;canvas&#xff09;标签/元素主要使用CSS实现。 使用画布&#xff08;canvas&#xff09;标签/元素实现&#xff0c;下面&#xff0c;在画布上…

Compose | UI组件(十四) | Navigation-Data - 页面导航传递数据

文章目录 前言传参流程实例说明普通方式传值定义接受参数格式定义接受参数类型获取参数传入参数传参和接受参数效果图 结合 ViewModel 传递参数定义ViewModel在 navigation 定义 ViewModel 实例&#xff0c;并且传入 LoginScreen传入输入框中的值&#xff0c;并且跳转传值获取值…

Spring Authorization Server Spring Security密码加密

文章目录 一、修改密码编码器二、效果三、注意点1. RegisteredClient2. UserDetailsService 一、修改密码编码器 以BCryptPasswordEncoder举例。 直接将其注册成PasswordEncoder 的Bean即可。 Beanpublic PasswordEncoder passwordEncoder() {// 密码为明文方式 // ret…

“智能检测,精准把控。温湿度检测系统,为您的生活带来全方位的健康保障。”#非标协议项目【下】(分文件编程)

“智能检测&#xff0c;精准把控。温湿度检测系统&#xff0c;为您的生活带来全方位的健康保障。”#非标协议项目【下】&#xff08;分文件编程&#xff09; 前言预备知识1温湿度检测系统需求2.分文件编程核心思路3.分文件编程操作4利用分文件操作建立uart.c、lcd1602.c、dht11…

【图形图像的C++ 实现 01/20】 2D 和 3D 贝塞尔曲线

目录 一、说明二、贝塞尔曲线特征三、模拟四、全部代码如下​五、资源和下载 一、说明 以下文章介绍了用 C 计算和绘制的贝塞尔曲线&#xff08;2D 和 3D&#xff09;。    贝塞尔曲线具有出色的数学能力来计算路径&#xff08;从起点到目的地点的曲线&#xff09;。曲线的形…

YOLOv8改进 | 检测头篇 | 独创RFAHead检测头超分辨率重构检测头(适用Pose、分割、目标检测)

一、本文介绍 本文给大家带来的改进机制是RFAHead,该检测头为我独家全网首发,本文主要利用将空间注意力机制与卷积操作相结合的卷积RFAConv来优化检测头,其核心在于优化卷积核的工作方式,特别是在处理感受野内的空间特征时。RFAConv主要的优点就是增加模型的特征提取能力,…

HiveSQL——sum(if()) 条件累加

注&#xff1a;参考文章&#xff1a; HiveSql面试题10--sum(if)统计问题_hive sum if-CSDN博客文章浏览阅读5.8k次&#xff0c;点赞6次&#xff0c;收藏19次。0 需求分析t_order表结构字段名含义oid订单编号uid用户idotime订单时间&#xff08;yyyy-MM-dd&#xff09;oamount订…

ChatGPT高效提问—prompt常见用法(续篇七)

ChatGPT高效提问—prompt常见用法&#xff08;续篇七&#xff09; 1.1 零样本、单样本和多样本 ​ ChatGPT拥有令人惊叹的功能和能力&#xff0c;允许用户自由向其提问&#xff0c;无须提供任何具体的示例样本&#xff0c;就可以获得精准的回答。这种特性被称为零样本&#x…