【Spring系列】- Spring事务底层原理

news2024/12/30 2:03:19

Spring事务底层原理

😄生命不息,写作不止
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 一个有梦有戏的人 @怒放吧德德
🌝分享学习心得,欢迎指正,大家一起学习成长!

在这里插入图片描述

文章目录

  • Spring事务底层原理
    • 前言
    • 实验准备
      • 配置文件
      • 准备数据表
    • Spring事务的底层原理
      • 原理
      • Spring事务失效
      • 失效原理
      • 解决方案
        • 方案一
        • 方案二
    • @Configuration底层原理
      • 贯穿逻辑
      • @Configuration原理

前言

昨天学习了bean生命周期底层原理,今天就来接着简单学习spring事务的底层理解。

实验准备

配置文件

首先在配置文件中配置jdbcTemplate和事务管理器,并且需要开启事务的注解@EnableTransactionManagement以及@Configuration注解

@ComponentScan("com.lyd")
@EnableTransactionManagement
@Configuration
public class ApplicationConfig {
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }
    @Bean
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true");
        dataSource.setUsername("root");
        dataSource.setPassword("12356");
        return dataSource;
    }
}

准备数据表

本次实验使用学生表,就简单几个字段。
在这里插入图片描述

Spring事务的底层原理

       我们在需要加上事务的方法上添加@Transactional注解,然后在此方法中使用jdbcTemplate去执行SQL语句,再来执行此方法,观察可以看到,事务真的回滚了。

@Component
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void test(){
        jdbcTemplate.execute("insert into student values (1, 'lyd', 18, '20183033210')");
       throw new NullPointerException();
    }
}

原理

       首先spring会调用代理对象,对于事务,代理对象会通过执行事务的切面逻辑。在这个切面逻辑,Spring会去判断是否含有@Transactional事务注解,如果有才会去开启事务。spring的事务管理器会新建一个数据库连接conn,紧接着会把conn.autocommit 设置为 false ,autocommit(自动提交),每次执行完SQL后就会立马提交,因此这里需要设置为false。spring默认是开启了自动提交,当SQL执行结束之后就会提交,当遇到异常的时候,由于前面的事务都已经提升,因此就没法回滚了,所以需要把自动提交给关闭了。最后在通过第一次创建的对象(Spring个生命周期中通过构造方法创造的对象)去执行test方法。接着会去执行SQL语句,在此SQL执行完之后是不会进行提交的,在执行SQL语句之前,jdbcTemplate会去拿到事务管理器创建的这个数据库连接conn。当执行完test方法后,Spring事务会去判断是否有异常,没有异常就会提交事务(conn.commit()),否者就会事务回滚(conn.rollback());
在这里插入图片描述

Spring事务失效

       接下来实现一个案例,在test方法中调用本类的另一个方法Add,两个方法都是执行了插入的SQL语句。两个方法都加上了@Transactional注解,只是在第二个方法中的注解标上一个策略:propagation = Propagation.NEVER,这个的意思是:总是非事务地执行,如果存在一个活动事务,则抛出异常。按道理来说以下代码执行将会出现异常,并且会回滚事务。

@Component
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void test(){
        jdbcTemplate.execute("insert into student values (1, 'lyd', 18, '20183033210')");
        doAdd();
    }
    @Transactional(propagation = Propagation.NEVER)
    public void doAdd() {
        jdbcTemplate.execute("insert into student values (2, 'lyy', 18, '20183033211')");
    }
}

可是,最后的结果却不是预期结果。
在这里插入图片描述

失效原理

       可见最后还是将两条数据插入了,显然这个事务是不回滚的,那么这是为什么呢?从上面说事务的底层原理就可以知道,当spring创建了代理对象,在代理对象内部的test方法中的切面逻辑,会去创建数据库连接等等,最后由普通对象(UserService.class通过构造方法去创建的对象)去执行test,也就是相当于是使用了普通对象去执行doAdd方法,普通对象就只是构造方法实例化的一个对象,执行doAdd并不会去检测这个@Transactional注解,因此这个事务就不会被执行到,也就不会回滚。然而spring应该是在执行代理类的test方法时候回去判断@Transactional注解,会有额外的逻辑去判断事务,也就是doAdd应该也要由代理对象去执行。

解决方案

那么有办法解决吗?办法肯定过有,接下来介绍一种解决方法

方案一

将add方法抽到另一个bean类里面

@Component
public class UserBaseService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.NEVER)
    public void doAdd() {
        jdbcTemplate.execute("insert into student values (2, 'lyy', 18, '20183033211')");
    }
}

再来通过bean对象来执行这个doAdd方法

@Component
public class UserService {
    private JdbcTemplate jdbcTemplate;
    private UserBaseService userBaseService;
        jdbcTemplate.execute("insert into student values (1, 'lyd', 18, '20183033210')");
        userBaseService.doAdd();
    }
}

然后把数据表清空,在此执行
在这里插入图片描述
这回就报错了,而且也是我们所期望的错误,在看一下数据表,发现数据没有保存进去。

方案二

       那如果我们还是想要在本类中去执行这个doAdd方法呢?其实也是可以,就是我们通过自己调用自己的方式,在本类中引用本类的bean对象,此时他就是一个代理对象,这样事务的策略也就能够实现了。

@Autowired
private UserService userService;

@Transactional
public void test(){
    jdbcTemplate.execute("insert into student values (1, 'lyd', 18, '20183033210')");
    throw new NullPointerException();
    userService.doAdd();
}

也能得到我们预期的实现效果,数据库中也没有相关数据。
在这里插入图片描述

@Configuration底层原理

在上面我们提到了jdbcTemplate获得数据库连接,那么这个又是如何得到呢?我们来看一下一开始的配置。

@Bean
public JdbcTemplate jdbcTemplate() {
    return new JdbcTemplate(dataSource());
}

@Bean
public PlatformTransactionManager transactionManager() {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(dataSource());
    return transactionManager;
}

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&useOldAliasMetadataBehavior=true");
    dataSource.setUsername("root");
    dataSource.setPassword("12356");
    return dataSource;
}

贯穿逻辑

       在创建 jdbcTemplate 的bean对象的时候,会去调用 dataSource ,在创建事务管理PlatformTransactionManager也会去调用一次dataSource方法,在这个方法中会创建新的dataSource,那么,所获得的事务不就不一样了吗,显然这是不行的。那么在spring中,他是如何实现单例的呢?就是通过 @Configuration 注解来实现。
       其实spring会通过ThreadLocal(线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构),在spring中ThreadLocal<Map<DataSource, conn>>是根据 DataSource来存储连接conn,如果没有 @Configuration注解来实现,两次使用的DataSource就是不同的。 jdbcTemplate在获取 DataSource 对象的时候,会去ThreadLocal的map根据jdbcTemplate自己的DataSource去找连接,然而这时候DataSource对象不同,他就找不到,就会自己从新生成连接,当执行完SQL语句之后,就会去提交,这时候接下来再抛异常已经没用了。

@Configuration原理

       然而,@Configuration能够实现单例DataSource对象呢?这就是因为@Configuration也是采用了动态代理会创建ApplicationConfig的代理对象。spring会创建ApplicationConfig的代理对象,这个代理对象会去调用jdbcTemplate方法,而代理对象会执行super.jdbcTemplate(),在这个方法中需要执行dataSource()方法,他会首先去容器中找是否有dataSource的bean对象,如果有直接返回,没有就会创建,创建之后会将单例进行保存。接着transactionManager方法也是由代理对象去执行的,在他需要dataSource对象的时候,也是现去容器查找,这就能够实现他们两个的dataSource是一样的了。这样事务拿到的数据库连接就是相同了,如果是在使用dataSource这个bean对象的时候,使用的beanName是不同,那么最后得到的连接也就不同。

👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍

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

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

相关文章

Vue-CLI的安装、使用及环境配置(超详细)

Vue CLI 是一个基于 Vue 进行快速项目开发的工具。它可以提供可交互式的项目脚手架和运行时的服务依赖&#xff0c;帮助你快速完成一个风格统一、拓展性强的现代化 web 单页面应用。 Vue-CLI 所需环境 Vue-CLI 是一个需要全局安装的NPM包&#xff0c;安装需要在 Node.js 环境下…

一、openCV+TensorFlow环境搭建

目录一、anaconda安装二、tensorflow安装三、Opencv安装四、pycharm新建项目使用Anaconda的环境五、验证环境安装六、tensorflow安装jupyter notebook一、anaconda安装 anaconda官网&#xff1a;https://www.anaconda.com/anaconda下载&#xff1a;https://repo.anaconda.com/…

【k8s】10.网络插件

文章目录一、etcd详解1、etcd的特点2、准备签发证书的环境二、网络插件原理1、flannel1.1 UDP模式&#xff08;性能差&#xff09;1.2 VXLAN模式&#xff08;性能较好&#xff09;1.3 host-gw模式&#xff08;性能最高&#xff09;2、calico插件3、总结一、etcd详解 etcd是Cor…

Redis_第二章_实战篇_第一节_ 短信登录

Redis_第二章_实战篇_第一节_ 短信登录 文章目录Redis_第二章_实战篇_第一节_ 短信登录短信登录1.1、导入黑马点评项目1.1.1 、导入SQL1.1.2、有关当前模型1.1.3、导入后端项目1.1.4、导入前端工程1.1.5 运行前端项目:1.2 、基于Session实现登录流程1.3 、实现发送短信验证码功…

ANDI数据集介绍|补充信息|2022数维杯国际赛C题

目录 1.患者基本信息 2.生物标记物量化值 3.认知评估 4.解剖结构量化值 5.Other 6.上述各信息的bl值 1.患者基本信息 RID (Participant roster ID) ex. 2、PTID (Original study protocol) ex. 011_S_0002、VISCODE (Visit code) ex. bl、SITE ex. 11、COLPROT (Study p…

服务拆分和远程调用(微服务)

博客主页&#xff1a;踏风彡的博客 博主介绍&#xff1a;一枚在学习的大学生&#xff0c;希望在这里和各位一起学习。 所属专栏&#xff1a;SpringCloud 文章创作不易&#xff0c;期待各位朋友的互动&#xff0c;有什么学习问题都可在评论区留言或者私信我&#xff0c;我会尽我…

课程设计 | 教学设备管理系统

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

SpringCloud微服务(二)——Eureka服务注册中心

Eureka服务注册中心 SpringCloud组件&#xff0c;Eureka已停更。 内容简介 1、Eureka是什么 Eureka 是 Netflix 开发的&#xff0c;一个基于 REST 服务的&#xff0c;服务注册与发现的组件&#xff0c;以实现中间层服务器的负载平衡和故障转移。服务注册&#xff1a;将服务…

[杂记]算法: 单调栈

0. 引言 单调栈, 顾名思义就是从栈底到栈顶元素单调递增或者单调递减的栈. 往往, 我们在解决寻找一个元素前面/后面的最远/最近处满足某条件的另一个元素的时候可以用到单调栈. 也是用两道算法题作为例子. 在这之前, 先简单写一下构造单调栈的模板. 如果我们需要从一个数组中…

ES6 入门教程 18 Iterator 和 for...of 循环 18.7 for...of 循环

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程18 Iterator 和 for...of 循环18.7 for...of 循环18.7.1 数组18.7.2 Set 和 Map 结构18.7.3 计算生成的数据结构18.7.4 类似…

供应叶酸PEG试剂Folic acid-PEG-Azide,FA-PEG-N3,叶酸-聚乙二醇-叠氮

1、名称 英文&#xff1a;Folic acid-PEG-Azide&#xff0c;FA-PEG-N3 中文&#xff1a;叶酸-聚乙二醇-叠氮 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Azide PEG Folic acid&#xff08;FA&#xff09; PEG 4、分子量&#xff1a;可定制&#xff0c;FA-PEG-N3 5…

Web安全之CTF测试赛

Web安全之CTF测试赛 1.000-我是谁 题目描述&#xff1a; 找到诈骗网站bsde.cn的域名注册人及邮箱&#xff0c;将域名注册人的126邮箱填写到下方答案框并点击送出 考察点&#xff1a;whois查询 whois查询网址&#xff1a; https://x.threatbook.com/ //微步在线 http://wh…

ES6 入门教程 17 Promise 对象 17.11 Promise.reject() 17.12 应用 17.13 Promise.try()

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程17 Promise 对象17.11 Promise.reject()17.12 应用17.12.1 加载图片17.12.2 Generator 函数与 Promise 的结合17.13 Promise…

ES6 入门教程 15 Proxy 15.2 Proxy 实例的方法 15.2.2 set() 15.2.3 apply()

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程15 Proxy15.2 Proxy 实例的方法15.2.2 set()15.2.3 apply()15 Proxy 15.2 Proxy 实例的方法 拦截方法的详细介绍。 15.2.…

【附源码】Python计算机毕业设计天润律师事务所管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

〖全域运营实战白宝书 - 运营角色认知篇③〗- 运营的底层逻辑是什么?

大家好&#xff0c;我是 哈士奇 &#xff0c;一位工作了十年的"技术混子"&#xff0c; 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 &#x1f4ac; 人生格言&#xff1a;优于别人,并不高贵,真正的高贵应该是优于过去的自己。&#x1f4ac; &#x1f4e…

【笔试题】【day24】

文章目录第一题&#xff08;完全二叉树的最多结点个数&#xff09;第二题&#xff08;哈夫曼树的带权路径长度&#xff09;第三题&#xff08;堆的重建&#xff09;第四题&#xff08;哈希映射的冲突&#xff09;第一题&#xff08;完全二叉树的最多结点个数&#xff09; 一棵…

UE4 几种蓝图通信的方法

根据视频&#xff08;UE4 几种蓝图通讯的方法&#xff09;所做笔记 目录 方法一&#xff1a;通过公有变量 方法二&#xff1a;通过“获取类的所有actor”节点 方法三&#xff1a;通过蓝图接口 关卡蓝图与蓝图通信 方法一&#xff1a;通过公有变量 步骤&#xff1a; 1.新建…

一款php开发的非常好的OA办公管理系统源码

一款基于TP5 HAdmin Mysql打造的简单实用的开源的企业办公系统框架。可以帮助解决企业办公项目60的重复工作&#xff0c;让开发更多关注业务逻辑。既能快速提高开发效率&#xff0c;帮助公司节省人力成本&#xff0c;同时又不失灵活性。使用本系统可以简单快速地开发出企业级的…

如何将 MATLAB 源代码导出成 Java 的 JAR 包

文章目录编写 MATLAB 源文件安装 Java制作 JAR 包找到 MATLAB 的 JAR 包运行环境&#xff1a; MATLAB R2022a Java 8&#xff08;1.8.0_311&#xff09; Windows 10 教育版 64位 使用混合编程通常都不是好主意&#xff0c;但是有时候会遇到极端的情况。Java 擅长网络编程&am…