当前位置: 首页 > news >正文

5. Spring 事务

文章目录

  • 1. Spring 事务简介
  • 2. Spring 事务角色
  • 3. Spring 事务属性
    • 3.1 事务配置
    • 3.2 案例:转账业务追加日志
    • 3.3 事务传播行为

1. Spring 事务简介

Spring 事务作用:在数据层或业务层保障一系列的数据库操作同成功、同失败。

数据层有事务我们可以理解,为什么业务层也需要处理事务呢?

举个简单的例子:
转账业务会有两次数据层的调用,一次是转入者加钱,一次是转出者减钱;
把事务放在数据层,加钱和减钱就有两个事务;
如果先加钱后减钱,且加钱之后其他地方出现异常,就会出现加钱成功减钱失败的结果。即没办法保证加钱和减钱同时成功或者同时失败;
这个时候就需要将事务放在业务层进行处理。

不用事务时,我们是这样处理的:

在这里插入图片描述

Dao接口

public interface AccountDao {@Update("update tbl_account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update tbl_account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);
}

Service 接口和实现类

public interface AccountService {/*** 转账操作* @param out 转出方* @param in 转入方* @param money 余额*/void transfer(String out, String in, Double money);
}@Service
public class AccountServiceImpl implements AccountService {@Autowired//按类型注入private AccountDao accountDao;@Overridepublic void transfer(String out, String in, Double money) {accountDao.outMoney(out, money);accountDao.inMoney(in, money);}
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() {accountService.transfer("Tom", "Jerry", 100D);}
}

上述代码运行时,会执行转账操作,Tom 的账户减少 100,Jerry 的账户增加 100。

这是正常情况下的运行结果,但是如果在转账的过程中出现了异常,如:

在这里插入图片描述

这个时候就模拟了转账过程中出现异常的情况,正确的操作应该是 Tom 和 Jerry 账户中的钱不变。但是真正运行后会发现,并没有像我们想象的那样,Tom账户中的钱变少了,而 Jerry 账户中的钱没变。

当程序出问题后,我们需要让事务回滚,且这个事务应该加在业务层上。Spring 的事务管理就是用来解决这类问题的。

Spring事务管理具体的实现步骤为:

(1) 在需要被事务管理的方法上添加注解

@Service
public class AccountServiceImpl implements AccountService {@Autowired//按类型注入private AccountDao accountDao;@Override@Transactional//开启事务public void transfer(String out, String in, Double money) {accountDao.outMoney(out, money);accountDao.inMoney(in, money);}
}

注意:
@Transactional 可以写在接口类上、接口方法上、实现类上和实现类方法上
写在接口类上,该接口的所有实现类的所有方法都会有事务
写在接口方法上,该接口的所有实现类的该方法都会有事务
写在实现类上,该类中的所有方法都会有事务
写在实现类方法上,该方法上有事务
建议写在实现类或实现类的方法上

(2) 在 JdbcConfig 类中配置事务管理器

public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Bean//把方法的返回值定义成一个beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}//配置事务管理器,mybatis使用的是jdbc事务@Bean//把方法的返回值定义成一个beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
}

注意:事务管理器要根据使用技术进行选择,Mybatis 框架使用的是 JDBC 事务,可以直接使用 DataSourceTransactionManager。

(3) 开启事务注解

@Configuration//该类是配置类
@ComponentScan("com.itheima")//扫描这个包下的类,找bean
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})//引入这些类中的bean
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {}

(4) 测试

会发现在转账业务出现错误后,事务就可以控制回滚,保证数据的正确性。

2. Spring 事务角色

本节重点理解两个概念,分别是事务管理员事务协调员

(1) 未开启Spring事务之前:
在这里插入图片描述

AccountDao 的 outMoney 因为是修改操作,会开启一个事务 T1;
AccountDao 的 inMoney 因为是修改操作,会开启一个事务 T2;
AccountService 的 transfer 没有事务。

运行过程中如果没有抛出异常,则 T1 和 T2 都正常提交,数据正确;
如果在两个方法中间抛出异常,T1 因为执行成功提交事务,T2 因为抛异常不会被执行,就会导致数据出现错误。

(2) 开启 Spring 事务管理后

在这里插入图片描述

transfer上添加了 @Transactional 注解,在该方法上就会有一个事务 T。
AccountDao 的 outMoney 方法的事务 T1 加入到 transfer 的事务 T 中;
AccountDao 的 inMoney 方法的事务 T2 加入到 transfer 的事务 T 中。
这样就保证他们在同一个事务中,当业务层中出现异常,整个事务就会回滚,保证数据的准确性。

通过上面例子的分析,可以得到如下概念:

事务管理员:发起事务方,在 Spring 中通常指代业务层开启事务的方法(如:transfer 方法)
事务协调员:加入事务方,在 Spring 中通常指代数据层方法(如:outMoney、inMoney 方法),也可以是业务层方法。

注意:
目前的事务管理的前提是 DataSourceTransactionManager 和 SqlSessionFactoryBean 使用的是同一个数据源。

3. Spring 事务属性

3.1 事务配置

在这里插入图片描述

上面这些属性都可以在@Transactional注解的参数上进行设置。

  • readOnly:true 只读事务,false 读写事务,增删改要设为 false,查询设为 true。
  • timeout:设置超时时间(单位:秒),在多长时间之内事务没有提交成功就自动回滚,-1 表示不设置超时时间。
  • rollbackFor:当出现指定异常时,进行事务回滚。
  • noRollbackFor:当出现指定异常时,不进行事务回滚。
  • rollbackForClassName 等同于 rollbackFor,只不过属性为异常的类全名字符串。
  • noRollbackForClassName 等同于 noRollbackFor,只不过属性为异常的类全名字符串。
  • isolation 设置事务的隔离级别
    DEFAULT:默认隔离级别, 会采用数据库的隔离级别
    READ_UNCOMMITTED:读未提交
    READ_COMMITTED:读已提交
    REPEATABLE_READ:重复读取
    SERIALIZABLE:串行化

rollbackFor 是当出现指定异常时,进行事务回滚;对于异常事务不应该都回滚么,为什么还要指定?

这块需要更正一个知识点,并不是所有的异常都会回滚事务,比如下面的代码就不会回滚,导致转出账户减钱成功,转入账户加钱失败。

在这里插入图片描述

出现这个问题的原因是:Spring 事务只对 Error 异常和 RuntimeException 异常及其子类进行事务回滚,其他的异常类型不回滚。IOException 就是其他的异常类型,所以不回滚。

此时就可以使用 rollbackFor 属性来设置 IOException 异常回滚:

在这里插入图片描述

3.2 案例:转账业务追加日志

需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕
需求微缩:A账户减钱,B账户加钱,数据库记录日志

分析:
①:基于转账操作案例添加日志模块,实现数据库中记录日志
②:业务层转账操作(transfer),调用减钱、加钱与记录日志功能

实现效果预期:
无论转账操作是否成功,均进行转账操作的日志留痕

该环境是基于转账环境来完成的,下面在其基础上继续往下写:
在这里插入图片描述

(1) 创建日志表

create table tbl_log(id int primary key auto_increment,info varchar(255),createDate datetime
)

(2) 添加 LogDao 接口

public interface LogDao {@Insert("insert into tbl_log (info,createDate) values(#{info},now())")void log(String info);
}

(3) 添加 LogService 接口与实现类

public interface LogService {void log(String out, String in, Double money);
}@Service
public class LogServiceImpl implements LogService {@Autowiredprivate LogDao logDao;@Transactionalpublic void log(String out,String in,Double money ) {logDao.log("转账操作由"+out+"到"+in+",金额:"+money);}
}

(4) 在转账的业务中添加记录日志

@Service
public class AccountServiceImpl implements AccountService {@Autowired//按类型注入private AccountDao accountDao;@Autowiredprivate LogService logService;//添加日志相关@Override//开启事务,出现IOException时回滚@Transactionalpublic void transfer(String out, String in, Double money){try{accountDao.outMoney(out,money);//int i =1/0;accountDao.inMoney(in,money);}finally {logService.log(out,in,money);//添加日志相关}}
}

在这里插入图片描述

当程序正常运行,tbl_account 表中转账成功,tbl_log 表中日志记录成功;
当转账业务之间出现异常(int i =1/0),转账失败,tbl_account 成功回滚,但 tbl_log 表未添加数据。

这个结果和我们想要的不一样,什么原因?该如何解决?
失败原因:日志的记录与转账操作隶属同一个事务,同成功同失败。

在这里插入图片描述
理想效果:无论转账操作是否成功,日志必须保留

3.3 事务传播行为

前面讲过 Spring 事务会把 T1、T2、T3 都加入到事务 T 中。
所以当转账失败后,所有的事务都回滚,导致日志没有记录下来。
这与需求不符,此时就想能不能让 log 方法单独是一个事务呢。

在这里插入图片描述

要想解决这个问题,就需要用到事务传播行为。
事务传播行为:事务协调员对事务管理员所携带事务的处理态度。

具体如何解决,就需要用到 propagation 属性。

@Service
public class LogServiceImpl implements LogService {@Autowiredprivate LogDao logDao;//propagation设置事务属性:传播行为设置为当前操作需要新事务@Transactional(propagation = Propagation.REQUIRES_NEW)public void log(String out,String in,Double money) {logDao.log("转账操作由"+out+"到"+in+",金额:"+money);}
}

经过上面的修改,当转账失败时,tbl_log 表中的日志记录也会成功。但是记录的内容也是 “转账操作由Tom到Jerry,金额:50.0”,这显然不合理。于是 AccountServiceImpl 中做如下修改:

@Service
public class AccountServiceImpl implements AccountService {@Autowired//按类型注入private AccountDao accountDao;@Autowiredprivate LogService logService;//添加日志相关@Override@Transactionalpublic void transfer(String out, String in, Double money){try{accountDao.outMoney(out,money);//int i = 1/0;accountDao.inMoney(in,money);logService.log(out,in,money);//添加日志相关}catch (Exception e){logService.log(out,in+"失败",0D);//添加日志相关throw new RuntimeException();//使事务回滚}}
}

这样,转账失败时,记录的日志就变为 “转账操作由Tom到Jerry失败,金额:0.0“。

【注意】当 catch 捕获了异常时,事务不会回滚。如果非得写 catch,需要 catch 后 throw new RuntimeException() 让事务回滚。

事务传播行为的可选值:
在这里插入图片描述

相关文章:

5. Spring 事务

文章目录1. Spring 事务简介2. Spring 事务角色3. Spring 事务属性3.1 事务配置3.2 案例:转账业务追加日志3.3 事务传播行为1. Spring 事务简介 Spring 事务作用:在数据层或业务层保障一系列的数据库操作同成功、同失败。 数据层有事务我们可以理解&am…...

【堆】数据结构堆的实现(万字详解)

前言: 在上一期中我们讲到了树以及二叉树的基本的概念,有了之前的认识,今天我们将来具体实现一种二叉树的存储结构“堆”!!! 目录1.二叉树顺序结构介绍2.堆的概念及结构3.调整算法3.1向上调整算法3.1.1算法…...

Docker进阶 - 9. docker network 之自定义网络

1. 运行两个tomcat实例,并进入容器内部 docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8 docker exec -it tomcat81 bashdocker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-idk8 docker exec -it tomcat82 bash2. ping一下各自的ip…...

springcloud-工程创建(IDEA)

文章目录介绍springcloud 常用组件1.创建父工程2.删除父工程的src目录3.修改父工程的pom文件4 springcloud 版本依赖5.创建子模块6 子项目下创建启动类介绍 Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具,它为开发中的配置管理、服务发现、断路器、智…...

Blender——物体的随机分布

问题描述将正方体随机分布在平面上。问题解决点击编辑-->偏好设置。在【插件】中的【物体】类型中勾选【Object: Scatter Objects】。右下的活动工具与工作区设置中就会出现【物体散列】的模块,可以调节各参数。选中正方体,按着Shift,选中…...

一文教你玩转 Apache Doris 分区分桶新功能

数据分片(Sharding)是分布式数据库分而治之 (Divide And Conquer) 这一设计思想的体现。过去的单机数据库在大数据量下往往面临存储和 IO 的限制,而分布式数据库则通过数据划分的规则,将数据打散分布至不同的机器或节点上&#xf…...

Spring JdbcTemplate 和 事务

JdbcTemplate概述 JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和,操作nosql数据库的RedisTemplate,操作消息…...

C/C++:程序环境和预处理/宏

程序的翻译环境和执行环境 在ANSI C的任何一种实现中,存在两个不同的环境。第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境,它用于实际执行代码。 编译和链接 一份源代码(比如test.c)需要通过编译&#xf…...

什么是死锁?死锁产生的四个必要条件是啥?如何避免和预防死锁的产生?

点个关注,必回关 文章目录什么是死锁死锁产生的原因:1、系统资源的竞争2、进程运行推进顺序不当引起死锁产生死锁的四个必要条件:死锁的避免与预防什么是死锁 死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此…...

工程管理系统源码-物料管理-工程项目管理系统-建筑施工管理软件

如今建筑行业竞争激烈,内卷严重,发展趋势呈现两极分化,中小微企业的生存空间被逐步压缩,利润逐年减少。事实证明,工地上粗放式的人管人管理模式已经落于时代,劳动力纠纷和物料浪费现象尤其普遍,…...

Roboguide与TIA V16通讯

软件需求:1. roboguide;2. TIA V16;3. KEPServer; 在之前的文章中介绍过KEPServer与TIA V16的通讯,此处不再介绍。接下来,介绍roboguide与KEPServer的仿真通讯。 创建一个roboguide项目。选择【外部设备】➡【添加外部设备】 选择【OPC Server】➡【OK】 OPC服务器名称命…...

利用PyTorch深度学习框架进行多元回归

目录前言数据加载数据转换模型定义模型训练模型评估模型保存与加载完整代码讨论参考文献前言 大多数数据科学家以往经常常是利用传统的机器学习框架sklearn搭建多元回归模型,随着深度学习和强化学习越来越普及,很多数据科学家尝试使用深度学习框架来进行…...

EBS常用接口开发

整理了一些工作中常用的Oracle EBS接口和API,最早是看着大神黄建华文档起来的,格式内容参考他的文档,加上一些自己开发的程序和经历而已。 PO PO接收、检验、入库、退货-InterfaceAPI_刘文钊1的博客-CSDN博客 基础数据 EBS物料、bom、工艺导入…...

【完整】UR机械臂逆运动学求解过程及c++代码实现

有任何问题请在评论区留言,我尽可能的回复大家 一. 逆运动学的求解需要以下数学运算 利用DH参数得到每个关节的变换矩阵;利用变换矩阵求出机械臂整个链的变换矩阵;求出末端位姿;利用已知末端位姿和整个链的变换矩阵,…...

68. Python的相对路径

68. Python的相对路径 文章目录68. Python的相对路径1. 知识回顾2. 什么是相对路径3. 相对路径的语法4. 查看相对路径的方法5. 写出所有txt文件的相对路径5.1 同目录5.2 上级目录6. 用相对路径读取txt文件6.1 读取旅游.txt6.2 读取旅游经费.txt6.3 读取笔记.txt和new.txt6.4 读…...

java数据类型

数据类型 类型分类,存储范围,字面量,默认值,类型转换 类型分类 存储范围 数据类型字节数表示范围byte1-128~127short2-32768~32767,正负3万左右int4-2147483648~2147483647,正负21亿左右long8-922337203…...

Kotlin 替换非空断言的几种方式

Kotlin 出现断言的两种情形 IDE java 与 kotlin 自动转换时,自动添加非空断言的代码Smart Cast 失效 代码展示: class JavaConvertExample {private var name: String? nullfun init() {name ""}fun foo() {name null;}fun test() {if (…...

2023年了,来试试前端格式化工具

在大前端时代,前端的各种工具链穷出不断,有eslint, prettier, husky, commitlint 等, 东西太多有的时候也是trouble😂😂😂,怎么正确的使用这个是每一个前端开发者都需要掌握的内容,请上车🚗&…...

spring cloud 企业工程项目管理系统源码+项目模块功能清单

工程项目各模块及其功能点清单 一、系统管理 1、数据字典:实现对数据字典标签的增删改查操作 2、编码管理:实现对系统编码的增删改查操作 3、用户管理:管理和查看用户角色 4、菜单管理:实现对系统菜单的增删改查操…...

TCP分片解析

本文目录什么是IP分片为什么会产生IP分片为什么要避免IP分片如何避免IP分片什么是IP分片 IP协议栈将TCP/UDP传输层要求它发送的,但长度大于发送端口MTU的一个数据包,分割成多个IP报文后分多次发送。这些分成多次发送的多个IP报文就是IP分片。 为什么会…...

开发了一款基于 Flask 框架的在线电影网站系统(附 Python 源码)

文章目录前言项目介绍源码获取运行环境安装依赖库项目截图首页展示图视频展示页视频播放页后台管理页整体架构设计图项目目录结构图前台功能模块图后台功能模块图本地运行图前言 今天我给大家分享的是基于 Python 的 Flask 框架开发的在线电影网站系统,大家平时需要…...

如何获得CSM--敏捷教练证书

1、什么是CSM?CSM即Certified Scrum Master,Scrum Master负责确保所有人都能正确地理解并实施Scrum,确保Scrum团队遵循Scrum的理论、实践和规则。Scrum Master是Scrum团队中的服务型领导,帮助Scrum团队外的人员了解他们如何与Scrum团队交互是…...

Java面试数据库

目录 一、关系型数据库 数据库权限 表设计及创建 表数据相关 数据库架构优化 二、非关系型数据库 redis 今天给大家稍微整理了一下,内容有数据表设计的三大范式原则、sql查询如何优化、redis数据的击穿、穿透、雪崩等...,以及相关的面试题&#xff0…...

关于进行vue-cli过程中的解决错误的问题

好久没发文章了,直到今天终于开始更新了,最近想进军全端,准备学习下vue,但是这东西真的太难了,我用了一天的时间来解决在配置中遇到的问题!主要问题:cnpm文件夹和vue-cli文件夹的位置不对并且vu…...

Rockchip Linux USB Gadget

一:概述 USB Gadget 是运行在 USB Peripheral 上配置 USB 功能的子系统,正常可被枚举的 USB 设备至少有 3 层逻辑层,有些功能还会在用户空间多跑一层逻辑代码。Gadget API 就是具体功能和硬件底层交互的中间层。从上到下,逻辑层分布为: USB Controller: USB上最底层的软…...

Linux -文件系统操作与帮助命令

1、Linux -文件系统操作 df — 查看磁盘的容量 df -h —以人类可以看懂的方式显示磁盘的容量,易读 du 命令查看目录的容量 # 默认同样以块的大小展示 du # 加上 -h 参数,以更易读的方式展示 du -h-d 参数指定查看目录的深度: # 只查看 1…...

UMI 创建react目录介绍及配置

UMI 生成react项目目录介绍及配置 react项目目录介绍umi多种配置方案运行时配置app.ts 的使用 1、umi创建的项目目录大致如下 ├─package.json 配置依赖以及启动打包所需的命令 ├─.umirc.ts 配置文件,包含 umi 内置功能和插件的配置 ├── dist 打包后生成的…...

基于matlab使用机器学习和深度学习进行雷达目标分类

一、前言此示例展示了如何使用机器学习和深度学习方法对雷达回波进行分类。机器学习方法使用小波散射特征提取与支持向量机相结合。此外,还说明了两种深度学习方法:使用SqueezeNet的迁移学习和长短期记忆(LSTM)递归神经网络。请注…...

Protocol Buffers V3语法全解

目录protobuf介绍protobuf使用protoc命令语法定义消息类型指定字段类型分配字段编号指定字段规则添加更多消息类型注释保留字段从.proto文件生成了什么?值类型默认值枚举使用其他消息类型导入定义嵌套类型更新消息类型未知字段any任意类型oneofoneof 特性兼容性问题…...

MediaPipe之人体关键点检测>>>BlazePose论文精度

BlazePose: On-device Real-time Body Pose tracking BlazePose:设备上实时人体姿态跟踪 论文地址:[2006.10204] BlazePose: On-device Real-time Body Pose tracking (arxiv.org) 主要贡献: (1)提出一个新颖的身体姿态跟踪解决…...

网站建设公司做前端/网站建站在线制作

/** 【需求】服务端接收客户端发送过来的数据,并打印在控制台上。* * 建立TCP服务端的思路:* * 1.创建服务端Socket服务,通过ServerSocket。* * 2.服务端必须对外提供一个端口,否则客户端无法连接。* (连接服务器&…...

wordpress调用 php文件上传/百度下载安装到手机

事件: 由于前一天的晚上加班了、第二天又接着上班、所以精神上有点不在状态;收到客户的反馈说在slave上找不到master刚刚插入的数据; 阶段1: 遇到这事的第一感觉就是这可能是主从延时、或是slave的复制出错了使得数据没有同步、于…...

设计小程序多少钱/seo推广小分享

Activity之间传数据时,为了避免麻烦,往往会将一些值封装成对象,然后将整个对象传递过去。传对象的时候有两种情况,一种是实现Parcelable接口,一种是实现Serializable接口。0、解释两种接口:1)实…...

建行手机银行下载app最新版/网络优化器下载

Verilog时序逻辑硬件建模设计(五)异步计数器&总结-Asynchronous Counter Design没有任何寄存器逻辑,RTL设计是不完整的。RTL是寄存器传输级或逻辑,用于描述依赖于当前输入和过去输出的数字逻辑。在异步计数器中,时…...

哪里找专业做网站的人常熟/找个免费的网站

2019独角兽企业重金招聘Python工程师标准>>> 1 集操作 定义:把多个sql的结果集,通过逻辑上的整合运算,拼在一起显示。 集操作缺省下都是按第一个查询的第一列升序排序,当然除了union all: …...

做网站卖东西赚钱么/百度导航怎么下载

PHP的网站主要攻击方式: 1、命令注入(Command Injection)2、eval注入(Eval Injection)3、客户端脚本攻击(Script Insertion)4、跨网站脚本攻击(Cross Site Scripting, XSS)5、SQL注入攻击(SQL injection)6、跨网站请求伪造攻击(Cross Site Request Forgeries, CSRF)…...