- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Spring @Transaction 注解执行事务的流程由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
相信小伙伴一定用过 @Transactional 注解,那 @Transactional 背后的秘密又知道多少呢?
Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢?
在开始 debug 阅读源码之前,小伙伴们应该已经知道 MySQL 是如何开启事务的.
因此可以得出猜测:
那下面跟着源码一起读一读,Spring 的 @Transactional 注解是如何执行事务逻辑的?
这里使用的是 Spring Boot + MySQL + Druid 。
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version></dependency>
UserService
基于 AOP 生成代理对象;AbstractAutowireCapableBeanFactory#initializeBean ... wrapIfNecessary AbstractAutoProxyCreator#createProxy CglibAopProxy#getProxy 生成代理对象 。
开始执行 userService.updateUserInfo(); 这里的 userService 就是代理对象;会被 CglibAopProxy.DynamicAdvisedInterceptor#intercept 方法拦截; 。
TransactionInterceptor#invoke 被事务拦截器拦截 。
TransactionAspectSupport#invokeWithinTransaction 事务处理 。
AbstractPlatformTransactionManager#getTransaction 会在这里调用 AbstractPlatformTransactionManager#startTransaction 方法,来开启事务.
是不是看到 doBegin 这个词突然感觉很熟悉.
跟进 DataSourceTransactionManager#doBegin 方法,注意看,此时是在 spring-jdbc-5.3.8.jar 包下面的.
因为使用的 druid 连接池,所以这块 Connection 是 durid 的连接池.
DruidPooledConnection#setAutoCommit(false)
关闭自动提交;这里就是 druid 的逻辑,一顿执行然后到 com.alibaba.druid.filter.FilterChainImpl#connection_setAutoCommit.
ConnectionImpl#setAutoCommit
,这个是在 mysql-connector-java-8.0.25.jar
包下的。
这一句才是重点 SET autocommit=0.
SET autocommit=0
开启事务了! 。
总结一下流程:
在开始事务之后,会通过回调执行方法的内部逻辑.
因为这里使用的是 Mybatis,所以还是会被代理,MapperProxy#invoke; 。
DruidPooledPreparedStatement#execute; 。
ClientPreparedStatement#execute; 。
执行过程相对比较简单:
在 TransactionAspectSupport#invokeWithinTransaction 最后一行,commitTransactionAfterReturning(txInfo); 就是提交事务.
AbstractPlatformTransactionManager#commit 抽象事务管理器,进行提交事务 。
DataSourceTransactionManager#doCommit 数据源数据管理器,提交事务 。
这里肯定是调用连接池的方法,所以会执行到 DruidPooledConnection 中 。
DruidPooledConnection commit 。
最终还是执行到 mysql-connector-java-8.0.25.jar 包下面的 ConnectionImpl#commit 。
调用 commit 提交事务.
commit
异常在这里 TransactionAspectSupport#invokeWithinTransaction 会被 catch.
AbstractPlatformTransactionManager#rollback 在这里进行 rollback 。
执行 DataSourceTransactionManager#doRollback 。
最终执行到 mysql-connector-java-8.0.25.jar 的 ConnectionImpl#rollback() 到 ConnectionImpl#rollbackNoChecks 。
从而执行 rollback 语句 。
rollback
cleanupTransactionInfo(txInfo),
在 这个方法中会将之前设置的 autocommit 进行恢复.
如果觉得这样有点绕,那咱们可以看简单版本的,不带 Spring.
/** * @author liuzhihang * @date 2021/6/18 16:51 */public class MainTest { public static void main(String[] args) throws Exception { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/demo"); dataSource.setUsername("root"); dataSource.setPassword("root"); Connection connection = dataSource.getConnection(); try { // 关闭自动提交 connection.setAutoCommit(false); connection.prepareStatement("update user_info set user_name = 'liuzhihang' where user_id = '1001';").executeUpdate(); connection.prepareStatement("update user_address set address = 'anhui' where user_id = '1001';").executeUpdate(); // 提交事务 connection.commit(); } catch (Exception e) { // 回滚 connection.rollback(); } finally { // 开启自动提交 connection.setAutoCommit(true); } }}
看完 Java 原生提交事务的方式,是不是感觉简单明了.
Spring @Transactional 只是创建了 AOP 代理,通过代理调用原生的开启关闭事务,同样在执行 SQL 那一块,也是 Mybatis 进行了代理,从而提交 SQL.
最后,将图进行合并,总结流程.
至此,事务执行过程分析完毕.
不过还是有一个疑问?
为什么使用 set autocommit = 0 来开启事务,而不是使用 begin 或者 start transaction 来开启事务呢?
以上就是Spring @Transaction 注解执行事务的流程的详细内容,更多关于Spring @Transaction 注解的资料请关注我其它相关文章! 。
原文链接:https://www.cnblogs.com/liuzhihang/p/14929912.html 。
最后此篇关于Spring @Transaction 注解执行事务的流程的文章就讲到这里了,如果你想了解更多关于Spring @Transaction 注解执行事务的流程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
首先是一些背景;我们正在开发一个数据仓库,并对我们的 ETL 过程使用哪些工具进行一些研究。该团队非常以开发人员为中心,每个人都熟悉 C#。到目前为止,我已经看过 RhinoETL、Pentaho (
我需要具有管理员权限的进程。从this问题和答案来看,似乎没有比启动单独进程更好的方法了。因为我宁愿有一个专用于该过程的过程,而不是仅为此方法在第二个过程中启动我的原始应用程序–我以为我会在VS201
我有这个函数来压平对象 export function flattenObject(object: Object, prefix: string = "") { return Object.key
我正在开发一个基于java的Web应用程序,它要求我使用来自SIP( session 启动协议(protocol))消息的输入生成序列图。我必须表示不同电话和相应服务器之间的调用流程。我可以利用任何工
这是我的代码: Process p=Runtime.getRuntime().exec("something command"); String s; JFrame frame = new JFram
我对 istio 的 mTLS 流程有点困惑。在bookinginfo 示例中,我看到服务通过http 而不是https 进行调用。如果服务之间有 mTLS 那么服务会进行 http 调用吗? 是否可
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
之前做过一个简单的纸牌游戏,对程序的整体流程有自己的想法。我最关心的是卡片触发器。 假设我们有一张名为“Guy”的牌,其效果为“每当你打出另一张牌时,获得 2 点生命”。我将如何将其合并到我的代码中?
我有 4 个 Activity 。 A、B、C 和 D。 用户可以从每个 Activity 开始任何 Activity 。 即 Activity A 有 3 个按钮来启动 B、C 和 D。以同样的方式
我做了一个简单的路由器类,简化后看起来像这样 // @flow import { Container } from 'unstated' type State = { history: Objec
我有两个 Activity ,比如 A1 和 A2。顺序为 A1->A2我从 A1 开始 A2 而没有在 A1 中调用 finish() 。在 A2 中按下后退按钮后,我想在 A1 中触发一个功能。但
我正在考虑在我的下一个项目中使用 BPEL。我试用了 Netbeans BPEL 设计器,我对它很满意。但在我决定使用 BPEL 之前,我想知道它对测试驱动开发的适用程度。不幸的是,我对那个话题知之甚
我需要将两个表格堆叠在一起,前后都有内容。我无法让后面的内容正常流动。堆叠的 table 高度可变。 HTML 结构: ... other content ...
我是 Hibernate 的新手。我无法理解 Hibernate 的流程。请澄清我的疑问。 我有“HibernateUtil.java ”和以下语句 sessionFactory = new Anno
早上好 我开始使用 Ruby,想创建一个小工具来获取我的公共(public) IP 并通过电子邮件发送。我遇到了字符串比较和无法处理的 if/else block 的基本问题。 代码非常简单(见下文)
我目前正尝试在我的团队中建立一个开发流程并阅读有关 GitFlow 的信息。它看起来很有趣,但我可以发现一些问题。 让我们假设以下场景: 我们完成了 F1、F2 和 F3 功能,并将它们 merge
我已经使用 git flow 有一段时间了。我很想了解一个特定的用例。 对于我的一个项目,我有一张新网站功能的门票。此工单取决于许多子任务。我想为主工单创建一个功能分支,然后为每个子任务创建一个脱离父
你怎么知道在一个程序中已经发现并解决了尽可能多的错误? 几年前我读过一篇关于调试的文档(我认为这是某种 HOWTO)。其中,该文档描述了一种技术,其中编程团队故意将错误添加到代码中并将其传递给 QA
我目前正在构建一个微服务架构,并从身份验证服务器和客户端着手。我还想确认使用 token 对用户进行身份验证的最佳流程。 在上图中。第 3 步是我开始感到困惑。我想到了2个解决问题的方法。 每个 ap
我是一名优秀的程序员,十分优秀!