Spring05

一、Spring事务管理入门

1.1、创建数据库和表

        创建一个Spring数据库,在Spring数据库中创建tb_account(账户表),并初始化数据。

                                 

1.2、编写Service层、Mapper层以及调用层

        1.2.1、AccountServiceImpl实现了AccountService接口

        

        1.2.2、Mapper层中的代码 

         

        1.2.3、编写一个测试类

        

        1.2.4、 分析

                此时执行test3方法,可以看到结果

                

                我们发现张玮的账户中金额减少了500元,而张益达的账户中并没有增加500元。 这是因为AccountServiceImpl中故意写了int i=1/0这样的异常,所以张玮的账户减少了500元后,出现了异常,张益达的账户增加500元就没有执行。

                

1.2.5 、加上@Transactional注解

        我们在需要进行事务控制的方法上加上@Transactional,Spring就会自动帮我们进行事务的提交和回滚。

 

1.2.6、如果抛出的是其它编译时异常,仍然会提交事务 

        Spring 默认只有抛出运行时异常(即 RuntimeException 及子类)或 Error 及子类时才会回滚事务 。

        我们创建一个编译期异常,程序抛出异常,运行之后发现事务仍然提交了 ,解决办法:配置 rollbackFor = Exception.class

        表示:遇到所有异常都回滚

        

二、 Spring事务管理的原理

        通过日志我们发现,执行事务操作的对象是JdbcTransactionManager对象 ,并且为com.itheima.service.impl包下的AccountServiceImpl类中的transfer创建了事务。

2.1、JdbcTransactionManager 

               JdbcTransactionManager继承了DataSourceTransactionManager,

DataSourceTransactionManager中的dobegin()方法表示开始事务,doCommit方法表示提交事务doRollback表示回滚事务

2.2、Spring使用AOP的方式管理事务

        一旦我们进行了事务管理,也就是我们在Spring管理的类中或者方法上加了@Transactional注解,我们从容器中获取到的service就不是目标对象了而是代理对象,其内部通过AOP的方式对目标对象进行了增强,代理对象内部使用JdbcTransactionManager对象在切点方法执行前后进行事务管理

        以上面的AccountServiceImpl为例,在AccountServiceImpl中的transfer方法上面加了@Transactional注解,那么Spring就会为AccountServiceImpl创建代理对象,我们从Spring容器中拿到的就不是AccountServiceImpl这个目标对象,而是AccountServiceImpl的代理对象

        执行下面的测试方法

        获得结果:

          

        发现是通过SpringCGLIB的技术创建了 AccountServiceImpl的代理对象。

 

       

2.3、梳理整个代理流程

        在AccountServiceImpl中的transfer方法上面加了@Transactional注解,那么Spring就会为AccountServiceImpl创建代理对象。AccountServiceImpl的代理对象会重写AccountServiceImpl中的transfer方法,然后在AccountServiceImpl的代理对象中使用2.1中介绍的JdbcTransactionManager中的dobegin方法创建事务,doCommit方法提交事务,doRollback方法回滚事务

        JdbcTransactionManager中的这3个方法相当于通知方法(增强方法),分别位于AccountServiceImpl中切点方法前后,来完执行事务。

 

 

三、Spirng事务失效的常见场景 

3.1、 自己捕捉异常

        分析:自己 try-catch 异常,意味着代理对象认为没有发生异常,因此也会提交事务

        解决办法:业务方法内不要捕获异常或者将捕获的异常重新抛出。

3.2、 用在非public方法上

        众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。@Transactional注解只有用在public方法上面才会生效。

        AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null即不支持事务

 

 3.3、方法用final、static修饰,不会生效 

        spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。

        但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法也不能添加事务功能。

        如果某个方法是static的,同样无法通过动态代理,变成事务方法。

3.4、同一个类中的方法直接内部调用,会导致事务失效 

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public void add(UserModel userModel) {userMapper.insertUser(userModel);updateStatus(userModel);}@Transactionalpublic void updateStatus(UserModel userModel) {doSameThing();}
}

         由上面的代码我们可以看见在add方法中调用了updateStatus方法,updateStatus方法上使用了@Transactional注解,这种同一个类中的方法直接内部调用,会导致事务失效。

        我们知道Spring中的事务是通过创建目标对象的代理对象,来进行事务控制。add方法中的updateStatus(userModel);相当于this.updateStatus(userModel);相当于本类对象去调用updateStatus(userModel);而不是通过代理对象去调用updateStatus(userModel); 所以就会导致事务失效。

          解决办法:

1、在该Service类中注入自己(因为使用@Autowired prvate ServiceA serviceA;依赖注入的是代理对象)

可能会出现循环依赖的问题,具体的解决方法参考Spirng02中解决循环依赖。 

2、 新加一个Service方法

        通过新增一个ServiceB,并在ServiceB中的doSave方法里面依次执行add方法和update方法,并在ServiceA的save方法中使用ServiceB的对象调用dosave方法。(@Transactional注解加在ServiceB的dosave方法上)

3.5、(类本身) 未被spring管理 

        这种在方法上加了@Transactional注解,但是类上没有加上类似@Service注解的,事务也不会生效,因为Spring的事务本身是基于SpringAop的。

3.6、嵌套事务会造成事务回滚多了

public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);roleService.doOtherThing();}
}@Service
public class RoleService {@Transactional(propagation = Propagation.NESTED)public void doOtherThing() {System.out.println("保存role表数据");}
}

        我们在 UserServiceadd的方法上使用了@Transactional注解,并在add方法中调用了roleService.doOtherThing();

        RoleService中的doOtherThing()方法上也使用了@Transactional注解,并且propagation = Propagation.NESTED

        propagation表示传播的意思,Propagation.NESTED表示事务嵌套,这个在下面的事务的传播方式中会讲解。

        这样就造成了在add方法中调用了roleService.doOtherThing();而在RoleService中的doOtherThing()方法上事务的传播行为定义为嵌套,就造成了方法的嵌套。

        这种情况使用了嵌套的内部事务,原本是希望调用roleService.doOtherThing方法时,如果出现了异常,只回滚doOtherThing方法里的内容不回滚 userMapper.insertUser里的内容,即回滚保存点。但事实是,insertUser也回滚了

        因为doOtherThing方法出现了异常,没有手动捕获会继续往上抛,到外层add方法的代理方法中捕获了异常。所以,这种情况是直接回滚了整个事务,不只回滚单个保存点。

        解决办法

@Slf4j
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleService roleService;@Transactionalpublic void add(UserModel userModel) throws Exception {userMapper.insertUser(userModel);try {roleService.doOtherThing();} catch (Exception e) {log.error(e.getMessage(), e);}}
}

        可以将内部嵌套事务放在try/catch中,并且不继续往上抛异常。这样就能保证,如果内部嵌套事务中出现异常,只回滚内部事务,而不影响外部事务。

Spring事务失效的场景详见:

spring事务(注解 @Transactional )失效的12种场景_@transactional超时会不会抛出异常-CSDN博客

四、Spring中事务的传播行为 

                Spring中事务的传播行为默认的是REQUIRED,常见的就是REQUIRED和REQUIRES_NEW

 

4.1、Spring中事务的传播的演示 

         创建一个Service1,并在a方法上加上@Transactional注解,并在a方法中使用Service2的对象调用Service2中的b方法。

           创建一个Service2,并在b方法上加上@Transactional注解,那么在Service1中的a方法调用Service2中的b方法,使用的是a的事务还是b的事务呢?      

看日志得到答案 

1、首先创建Service1中a方法的事务 (Creating new transaction with name.......)

2、Service1.a().....(执行Service1中的a方法)

3、Participating in existing transaction(加入已经存在的事务,也就是Service1中a方法的事务)

4、 Service2.b().....(执行Service2中的b方法)

         

        上面演示的在a方法中调用同样加了@Transactional注解的b方法就是事务的传播,通过日志我们得出结论Spring中事务的传播行为默认的是REQUIRED(需要事务,有则加入,无则创建新事务 ),所以上面的例子中使用的是Service1中的a方法的事务。

        也就是说Service1中的a方法和Service2中的b方法同时使用Service1中的a方法,属于同意事务。

4.2、Service2中的b方法重新创建一个事务

        要想Service2中的b方法重新创建一个事务,只需要在Service2中的b方法上的@Transactional注解里面加上REQUIRES_NEW即可。

        

        再来查看日志

         

1、首先创建Service1中a方法的事务 (Creating new transaction with name.......)

2、Service1.a().....(执行Service1中的a方法)

3、Suspending current transaction,creating new transaction with...(暂停a方法中的事务,创建b方法的事务)

4、 Service2.b().....(执行Service2中的b方法)

 4.3、分析

         在Service2中的b方法上的@Transactional注解里面加上REQUIRES_NEW,表示需要新事务,无论有无,总是创建新事务,所以当Service1中的a方法调用Service2中的b方法时,也会创建Service2b方法的事务。

        此时Service2中的b方法和Service1中的a方法是两个不同的事务,他们之间是相互独立的。

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

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

相关文章

数据分析概述

目录 1.数据分析的基本类型:2.数据分析的实现方式:3.机器学习和统计学的区别:3.1统计学3.2机器学习 小结: 1.数据分析的基本类型: 这就不得不提到Gartner分析学价值扶梯模型了,这个模型从复杂度和价值两个…

端口开放问题

端口开放问题 所遇问题 在宿主主机上可以ping通虚拟机ip192.168.27.129,但无法在宿主主机上访问http://192.168.27.129:8080navavcat 16连接mysql时,2002 - Can’t connect to server on ‘192.168.27.129’(100601) 原因 以上两个问题&a…

树莓派4B使用ncnn部署yolov5-Lite,推理耗时 247ms 包含前后处理

一. 引言 最近在玩树莓派,想在树莓派上不是一个目标检测算法,大致看了一下,目前开源的大家都在使用yolov5-Lite,使用ncnn去推理加速,于是自己也尝试部署,在此记录一下,个人踩的坑。 二. 版本选…

Matlab三维绘图

绘制三维图plot3 t0:pi/50:10*pi; xsin(t); ycos(t); zt; plot3(x,y,z); 产生栅格数据点meshgrid 这个接口在绘制三维图像里面相当重要,很多时候要将向量变成矩阵才能绘制三维图。 x0:0.5:5; y0:1:10; [X,Y]meshgrid(x,y); plot(X,Y,o); x和y是向量,…

极值和平均值-第11届蓝桥杯选拔赛Python真题精选

[导读]:超平老师的Scratch蓝桥杯真题解读系列在推出之后,受到了广大老师和家长的好评,非常感谢各位的认可和厚爱。作为回馈,超平老师计划推出《Python蓝桥杯真题解析100讲》,这是解读系列的第22讲。 极值和平均值&…

GO语言笔记1-安装与hello world

SDK开发工具包下载 Go语言官网地址:golang.org,无法访问Golang中文社区:首页 - Go语言中文网 - Golang中文社区下载地址:Go下载 - Go语言中文网 - Golang中文社区 尽量去下载稳定版本,根据使用系统下载压缩包格式的安装…

【算法】 dp题单练习(寒假正在更新中)

题单链接: https://vjudge.net/contest/574209#overview 目录 1. 洛谷 P1020 导弹拦截 (dp二分Dilworth 定理) 2. 洛谷 P1439 最长公共子序列(二分求最长公共子序列) 3. 洛谷 P1854 花店橱窗布置 (线…

Illegal hex characters in escape (%) pattern

java.lang.NullPointerException 原因是关键字:5%葡萄糖注射液 其中的百分号通过HttpServletRequest的getParameter传到后端提示空指针异常,然后使用url格式,百分号的十六进制是%25(百分号加25) 在js代码中加入一段正…

Mybatis实现增删改查的两种方式-配置文件/注解

环境准备 1.数据库表tb_brand -- 删除tb_brand表 drop table if exists tb_brand; -- 创建tb_brand表 create table tb_brand(-- id 主键id int primary key auto_increment,-- 品牌名称brand_name varchar(20),-- 企业名称company_name varchar(20),-- 排序字段ordered int…

【SpringCloud Alibaba笔记】(2)Sentinel实现熔断与限流

Sentinel 概述 官网:https://github.com/alibaba/Sentinel 中文文档:https://sentinelguard.io/zh-cn/docs/introduction.html 类似Hystrix,以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热…

spdlog源码学习

前言 spdlog是一个跨平台c 的开源日志库 ,可以head only 使用,包含部分modern c 语法, 更是兼容了c20 format,支持异步和格式化输出,通俗易懂,适合阅读。 源码下载 here 用法 直接贴上了 example.cpp …

四种方式实现[选择性注入SpringBoot接口的多实现类]

最近在项目中遇到两种情况,准备写个博客记录一下。 情况说明:Service层一个接口是否可以存在多个具体实现,此时应该如何调用Service(的具体实现)? 其实之前的项目中也遇到过这种情况,只不过我采…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第二天-Linux开发板外设开发(物联技术666)

更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…

操作系统课程设计——文件管理系统(C语言版)

操作系统系列文章 http://t.csdnimg.cn/7XAnU 文章目录 实验一、进程的创建与撤销:http://t.csdnimg.cn/po4V0 实验二、银行家算法:http://t.csdnimg.cn/O5zoF 目录 操作系统系列文章 文章目录 文件管理 一、目的 二、设计内容 三、 设计要求 …

Excel·VBA按指定顺序排序函数

与之前写过的《ExcelVBA数组冒泡排序函数》不同,不是按照数值大小的升序/降序对数组进行排序,而是按照指定数组的顺序,对另一个数组进行排序 以下代码调用了《ExcelVBA数组冒泡排序函数》bubble_sort_arr函数(如需使用代码需复制…

如何在群晖7.2中运行WPS Office镜像容器并使用固定地址公网访问

文章目录 1. 拉取WPS Office镜像2. 运行WPS Office镜像容器3. 本地访问WPS Office4. 群晖安装Cpolar5. 配置WPS Office远程地址6. 远程访问WPS Office小结 7. 固定公网地址 wps-office是一个在Linux服务器上部署WPS Office的镜像。它基于WPS Office的Linux版本,通过…

LaTeX矩阵

在 LaTeX 中输入矩阵以及矩阵中增加公式。 LATEX 中 array 环境可以定义二维数组,具体需要定义列数,并用 \\ 换行,数组可作为一个公式块,在外套用 \left、\right 等定界符。 \mathbf{X} \left(\begin{array}{cccc}x_{11} &…

【AI】什么是大模型的偏见

目录 一、什么是大模型的偏见 二、偏见的危害 三、普通人可以做的一些偏见测试用例 1. 性别偏见测试: 2. 种族和民族偏见测试: 3. 职业偏见测试: 4. 年龄偏见测试: 5. 社会经济地位偏见测试: 6. 身体能力偏见…

Leetcode 3002. Maximum Size of a Set After Removals

Leetcode 3002. Maximum Size of a Set After Removals 1. 解题思路2. 代码实现3. 算法优化 题目链接:10037. Maximum Size of a Set After Removals 1. 解题思路 这一题的话我的思路就是分别以两个数组作为主数组,然后从中选择 n / 2 n/2 n/2个元素&…

接口限流方案

1.1 为什么要进行限流? 1.瞬时流量过高,服务被压垮? 2.恶意用户高频光顾,导致服务器宕机? 3.消息消费过快,导致数据库压力过大,性能下降甚至崩溃? 1.2 什么是限流 限流是对某一…