MySQL的事务以及springboot中如何使用事务

事务的四大特性:

概念:

事务 是一组操作的集合,它是不可分割的工作单元。事务会把所有操作作为一个整体,一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

 注意:

默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务。

事务的特性:
原子性:

  事务是最小的执行单元,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。不允许部分成功和失败。 

 一致性:

确保从一个正确的状态转换到另外一个正确的状态。举例,张三把钱转账给李四100元,张三少了100,李四多了100.但是他俩的钱加起来的钱数,还是和转账之前加起来的钱数相同。

隔离性:

并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。

持久性:

事务被提交之后,对数据库中数据的改变是持久的,即使数据库发生故障,也不会对其有影响。

操作:

事务执行的三步操作:

开启事务、提交事务/回滚事务

-- 开启事务
start transaction; / begin;
-- 1. 保存员工基本信息
insert into emp values (39, 'Tom', '123456', '汤姆', 1, '13300001111', 1, 4000, '1.jpg', '2023-11-01', 1, now(), now());
-- 2. 保存员工的工作经历信息
insert into emp_expr(emp_id, begin, end, company, job) values (39,'2019-01-01', '2020-01-01', '百度', '开发'),                                                              (39,'2020-01-10', '2022-02-01', '阿里', '架构');
-- 提交事务(全部成功) / 回滚事务(有一个失败)
commit; / rollback;

事务之间的相互影响:

脏读,不可重复读,幻读,丢失更新

 

通过我们的案例,来演示数据库事务:

在EmpController编写,添加员工的接口:

    /*** 新增员工的数据* @param emp* @return* @throws Exception*/@PostMappingpublic Result add(@RequestBody Emp emp) throws Exception {log.info("新增员工数据:{}",emp);empService.add(emp);return Result.success();}

编写EmpService接口:

    /*** 新增员工信息* @param emp*/void add(Emp emp) throws Exception;

编写 EmpServiceImpl的实现类:

    @Overridepublic void add(Emp emp) throws Exception {//先添加员工的基本信息emp.setCreateTime(LocalDateTime.now());  //赋值初始值emp.setUpdateTime(LocalDateTime.now());  //赋值初始值empMapper.addEmp(emp);//在添加员工的工作经历信息List<EmpExpr> exprList = emp.getExprList();if (!CollectionUtils.isEmpty(exprList)){  //当工作经历不是空的时候,在进行添加exprList.forEach(expr ->{expr.setEmpId(emp.getId());});}// 批量添加员工的经历empExprMapper.insertBatch(exprList);}

 注意,我们在添加员工的时候,还需要添加员工的工作经历。

@Insert("insert into emp (username, name, gender, phone, job, salary, image, entry_date, dept_id, create_time, update_time) values" +"(#{username},#{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime})")
void addEmp(Emp emp);

还需要编写 EmpExprMapper接口:

    /*** 批量添加员工经历的数据* @param exprList*/void insertBatch(List<EmpExpr> exprList);
主键返回:

在添加员工的工作经历的时候,我们还需要添加一个字段的值就是 emp_id 但是我们改怎么获取到这个刚添加好的主键id的??

主键返回:

在注解上使用:

@option注解

常用的属性值:

useGeneratedKeys:是否使用主键返回。

keyProperty:返回的id绑定那个属性

示例:

    @Options(useGeneratedKeys = true,keyProperty = "id") //使用主键返回,并把返回的主键赋值给id属性,emp对象的 id属性@Insert("insert into emp (username, name, gender, phone, job, salary, image, entry_date, dept_id, create_time, update_time) values" +"(#{username},#{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime})")void addEmp(Emp emp);
xml中使用:

<insert id="addUser"  useGeneratedKeys="true"  keyProperty="id">

        insert into tb_user(username,password) values(#{username},#{password})

</insert>

编写 EmpExprMapper接口:

    /*** 批量添加员工经历的数据* @param exprList*/void insertBatch(List<EmpExpr> exprList);

编写EmpExprMapper.xml

    <insert id="insertBatch">insert into emp_expr(emp_id,begin,end,company,job) values<foreach collection="exprList" item="expr" separator=",">(#{expr.empId},#{expr.begin},#{expr.end},#{expr.company},#{expr.job})</foreach></insert>

在Api测试我们成功添加了,员工的基本信息和员工的工作经历信息:

接下来,我做一些改动,添加员工的基本信息成功之后,手写一个运行时异常的bug

点击提交,然后看看会发生什么情况。

可以看到,员工的基本信息,添加成功了。

但是员工的工作经历,添加失败了。

为什么会失败呢?看看idea的看控制台

可以发现,控制台出现了异常 除0异常。

但是我们想一想,添加员工基本信息的时候,就要把员工的工作经历信息添加上去,这是才能保证数据的完整性和一致性,不能一个成功一个失败。

 这时我们想到了数据库的事务,如果添加员工和添加员工的工作经历都成功了,那么我们才向数据库执行提交 commit,如果其中一个失败了,我们就回滚事务。roooback

我们可以通过spring事务管理,来解决这个问题。

Spring事务管理:

事务控制:

注解: @Transactional

作用:将当前方法交给spring事务进行管理,方法执行前,开启事务,成功执行后提交事务。出现异常回滚事务。

位置:业务(service)层的方法上面,类上,接口上。

作用在接口上:

作用在类上:

作用在方法上:

    @Transactional@Overridepublic PageBean getList(Integer page, Integer pageSize) {PageHelper.startPage(page,pageSize);List<Emp> empList = empMapper.PageList();  //PageHelper后面的第一条SQL语句System.out.println(empList);Page<Emp> emps = (Page<Emp>) empList;return new PageBean(emps.getTotal(),emps.getResult());}

虽然在方法,接口还有实体类上面都可以添加@Transaction注解,但是我的建议是,在一个方法上面添加,因为有的操作,只涉及到了一张表的操作,也不用添加事务,就像添加一张表,要是成功就成功了,失败就失败了。不会保存在数据库里面的。因此不会造成数据的不完整性。

spring事务管理的日志输出:

 开启Spring事务管理的debug级别日志,就可以看到控制台中事务开启、提交、回滚的日志了 

在application.properties配置问价里面添加

开启spring事务管理的debug级别日志logging.level.org.springframework.jdbc.support.JdbcTransactionManager=debug

在添加员工的基本信息的方法上面添加注解:

   @Transactional@Overridepublic void add(Emp emp) throws Exception {try {//先添加员工的基本信息emp.setCreateTime(LocalDateTime.now());  //赋值初始值emp.setUpdateTime(LocalDateTime.now());  //赋值初始值empMapper.addEmp(emp);//在添加员工的工作经历信息List<EmpExpr> exprList = emp.getExprList();if (!CollectionUtils.isEmpty(exprList)){  //当工作经历不是空的时候,在进行添加exprList.forEach(expr ->{expr.setEmpId(emp.getId());});}int i = 1/ 0 ;// 批量添加员工的经历empExprMapper.insertBatch(exprList);}finally {EmpLog empLog = new EmpLog(null,LocalDateTime.now(),emp.toString());  //添加日志empLogService.insertLog(empLog);}}

在测试一下:

可以发现这个时候,已经报错了。

看看数据库里面添加成功里面数据没有:

可以发现员工的基本信息也没有添加进去,说明spring事务生效了。让我们来看一下控制台

在执行添加员工的信息的这个方法时,开始了事务,执行添加员工的基本信息后,SQL是执行成功的,但是里面出现了一个除0的异常,后面的添加员工的工作经历的SQL语句,就不执行了。所以在这一个事务中,一个执行成功了,一个执行失败了,事务就没有commit提交,而是rollback回滚了,所以我们在数据库里面并没有看到有数据添加到数据库里面的表中。

现在·我们把这个除0异常注释掉,看看程序执行会不会报错,数据能不能添加到数据库里面。

前后端联调:

可以看到数据添加成功了。

看看ideal的控制台

事务提交了

看看数据库里面的数据,emp表

在看看emp_expr表

 

数据也添加成功了

事务进阶:
属性-rollbackFor
  • 默认情况下,只有出现 RuntimeException 才回滚异常。
  • rollbackFor属性用于控制出现何种异常类型,回滚事务。

如果我们在代码中,添加一个编译时异常,这个时候,spring事务还会回滚吗?

我们可以测试一下,在添加员工的基本信息成功之后,在中间throws一个异常,然后在添加员工的工作经历信息。看看程序会发生什么。

  @Transactional@Overridepublic void add(Emp emp) throws Exception {try {//先添加员工的基本信息emp.setCreateTime(LocalDateTime.now());  //赋值初始值emp.setUpdateTime(LocalDateTime.now());  //赋值初始值empMapper.addEmp(emp);//在添加员工的工作经历信息List<EmpExpr> exprList = emp.getExprList();if (!CollectionUtils.isEmpty(exprList)){  //当工作经历不是空的时候,在进行添加exprList.forEach(expr ->{expr.setEmpId(emp.getId());});}if (true){throw new Exception();}// 批量添加员工的经历empExprMapper.insertBatch(exprList);}finally {EmpLog empLog = new EmpLog(null,LocalDateTime.now(),emp.toString());  //添加日志empLogService.insertLog(empLog);}}

服务器端出现异常:

看看数据库里面的数据。

员工的基本信息还是添加成功了

这是因为,默认情况下,只有出现 RuntimeException 才回滚异常。

可以发现在执行添加员工信息的时候,它commit提交了。

这个时候,需要在@Transaction注解里面添加 rollbaclFor属性了。

 

 @Transactional(rollbackFor = {Exception.class})     //开启事务 spring事务默认只能识别到运行时异常,要是想识别到Exception的异常,需要使用rollbackFor

我们在重启服务器测试:

发现数据添加失败了,但是数据库里面没有新增员工的基本信息。

数据表里面刚刚添加的数据。

我们把刚刚 手动写的异常删掉,在运行程序。

可以发现刚刚添加的数据成功了,

看看数据库里面的数据,emp表

查看emp_expr表。数据添加成功了。

 

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

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

相关文章

sylar高性能服务器-配置(P10-p11)代码解析+调试分析

文章目录 p9&#xff1a;配置模块搭建一、ConfigvarBase二、ConfigVar三、Config四、小结 p10&#xff1a;YAML的使用一、安装yaml-cpp二、使用yaml-cpp三、代码解析 P11&#xff1a;YAML与日志的整合一、方法函数二、代码调试三、test_config结果四、小结 p9&#xff1a;配置模…

18、责任链模式(Chain of Responsibility Pattern)

责任链模式&#xff0c;是用于避免请求发送者与多个请求处理者耦合在一起&#xff0c;让所有请求的处理者持有下一个对象的引用&#xff0c;从而将请求串联成一条链&#xff0c;在有请求发生时&#xff0c;可将请求沿着这条链传递&#xff0c;直到遇到该对象的处理器。用户只需…

华为或荣耀手机禁止强制升级鸿蒙系统的终极方法

需要有数据传输的usb线.打开usb调试模式. 进这个链接下载华为ADB一键卸载VS重装软件 按里面的视频说明,输入88 然后回车即可 https://download.csdn.net/download/viqecel/12161462

throw 用作异常规范

throw 用作异常规范 throw 关键字除了可以用在函数体中抛出异常&#xff0c;还可以用在函数头和函数体之间&#xff0c;指明当前函数能够抛出的异常类型&#xff0c;这称为异常规范&#xff08;Exception specification&#xff09;&#xff0c;有些教程也称为异常指示符或异常…

SVN版本回退

文章目录 SVN版本回退 SVN版本回退 一、revert to this version和revert to this version的区别&#xff1a; 基于4674版本执行"revert to this version"操作效果&#xff1a; 基于4674版本执行"revert changes from this version"操作效果&#xff1…

梯形速度规划算法原理及代码

梯形速度规划的原理&#xff1a;梯形速度规划算法 对应的代码如下&#xff1a; #pragma once #include <cmath> #include <iostream> #include <vector> struct SpeedPoint {SpeedPoint() {s 0;speed 0;t 0;}double s; // mdouble speed; // m/sdo…

推荐开源项目-网络应用协议框架Socket.D

基于事件和语义消息流的网络应用协议 Socket.D 0 代码仓库地址1 该开源项目特点2 项目结构3 核心理念-协议帧Frame4 结束语 0 代码仓库地址 https://gitee.com/noear/socketd 1 该开源项目特点 代码风格优雅文档说明齐全测试用例非常人性化上手快&#xff0c;代码用例很多代…

JDBC链接MySQL,实现对Goods表的增删改查并封装JDBC

项目目录结构 数据库配置 1.创建goods表 2.创建goods实体 package homework.MyJDBC;public class Goods {private int id;private String gId;private String gName;private float gPrice;private int gNum;public int getId() {return id;}public void setId(int id) {this.i…

Qt之自定义QToolTip,去掉显示动画和隐藏延时

一.效果 先来看看Qt原生QToolTip的缺点: 1.当提示内容无变化时,弹窗无法移动。只能先传个空字符串强制弹窗隐藏,然后在新位置再传个字符串。 If the text is the same as the currently shown tooltip, the tip will not move. You can force moving by first hiding the t…

晶圆划片机助力LED陶瓷基板高效切割:科技提升产业新高度

博捷芯半导体划片机在LED陶瓷基板制造领域&#xff0c;晶圆划片机作为一种先进的切割工具&#xff0c;正在为提升产业效率和产品质量发挥重要作用。通过精确的切割工艺&#xff0c;晶圆划片机将LED陶瓷基板高效地切割成独立的芯片&#xff0c;为LED产业的快速发展提供了有力支持…

ViT:视觉 Transformer

ViT&#xff1a;视觉 Transformer 网络结构Transformer 编码器MLP 头CNN 和 Transformer 网络结构 Transformer 的优势&#xff1a;注意力机制相当于一个多标签检索系统&#xff0c;位置嵌入能知道每个单词的位置&#xff0c;而且适合并行。 尝试把 Transformer 迁移到视觉领…

SpringBoot 面试题和答案,最新面经

覆盖了SpringBoot的方方面面。问题从SpringBoot的自动配置和起步依赖开始&#xff0c;接着深入到环境配置&#xff0c;再到异常处理、定时任务和异步处理等实用技能。每一个问题都能让你受到SpringBoot的强大和灵活性。 1. SpringBoot中核心特性有哪些&#xff1f; SpringBoo…

鸿蒙(HarmonyOS)应用开发——简易版轮播图

简述 轮播图在应用中&#xff0c;已经很常见的展现方式。像uniapp、iview&#xff0c;viewUI等前端组件框架&#xff0c;都提供了轮播图组件。那么在harmonyOS中&#xff0c;如果要实现轮播&#xff0c;我们是使用swiper 组件 swiper组件 swiper 组件是一种容器组件。它提供…

jmeter接口自动化测试通过csv文件读取用例并执行测试

最近在公司测试中经常使用jmeter这个工具进行接口自动化&#xff0c;简单记录下~ 一、在csv文件中编写好用例 首先在csv文件首行填写相关参数&#xff08;可根据具体情况而定&#xff09;并编写测试用例。脚本可通过优先级参数控制执行哪些接口&#xff0c;通过端口参数同时执…

【WebRTC】【Unity】Unity Web RTC1-Unity中简单实现远程画面

【项目资源下载】 本篇配套直接打开可用的项目包地址&#xff0c;欢迎下载&#xff1a; https://download.csdn.net/download/weixin_41697242/88612084 【背景】 想要在Unity中实现实时远程桌面&#xff0c;找到了Render Streaming这个手段&#xff0c;本篇介绍相应的使用方…

CSS层叠样式表一

1&#xff0c;CSS简介 1.1 CSS-网页的美容师 CSS的主要使用场景就是美化网页&#xff0c;布局页面的 CSS也是一种标记语言 CSS主要用于设置HTML页面中的文本内容&#xff08;字体&#xff0c;大小&#xff0c;对齐方式等&#xff09;、图片的外形&#xff08;宽高、边框样式…

三天精通Selenium Web 自动化 - Selenium(Java)环境搭建 (new)

0 背景 开发工具idea代码管理mavenjdk1.8webdriver chrome 1 chromedriver & chrome chromedriver和chrome要对应上&#xff1a; chomedriver下载地址&#xff1a;淘宝镜像 这里用的是 chromedriver88-0-4324-96.zipchrome下载地址&#xff1a;如何降级和安装旧版本的C…

【华为OD题库-083】玩牌高手-Java

题目 给定一个长度为n的整型数组&#xff0c;表示一个选手在n轮内可选择的牌面分数。选手基于规则选牌&#xff0c;请计算所有轮结束后其可以获得的最高总分数。 选择规则如下: 1.在每轮里选手可以选择获取该轮牌面&#xff0c;则其总分数加上该轮牌面分数&#xff0c;为其新的…

NC 比telnet 强大网络命令

1. 扫描端口 nc -vz google.com 80 使用nc -vz参数可以进行端口扫描&#xff0c;-v表示输出详细信息&#xff0c;-z表示只扫描端口而不发送数据。 nc -nvuz 82.156.125.169 666 nc -nvz 192.168.1.110 1521 --u udp nc -nvz 82.156.125.169 20-24 $ nc -vz -n …

代码随想录刷题题Day11

刷题的第十一天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C / Python Day11 任务 ● 理论基础 ● 递归遍历 ● 迭代遍历 ● 统一迭代 1 二叉树理论基础 1.1 二叉树的种类 &#xff08;1&…