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表。数据添加成功了。

 

属性-propagation:

事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

propagation常见的属性值和含义:

  • REQUIRED       [默认值]需要事务,有则加入,无则创建新事务
  • REQUIRES_NEW  需要新事务,,无论有无,都会创建新事务
  • SUPPORTS      支持事务,有则加入,无则在无事务状态中运行
  • NOT_SUPPORTED  不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
  • MANDATORY   必须有事务,否则抛异常
  • NEVER    必须没事务,否则抛异常

下面这个需求是在添加员工的时候,在添加员工的工作经历,然后在数据库里面添加日志信息。

操作日志的这张表,无论我在新增员工时,不管插入成功还是失败,我都要记录操作的日志。

准备实体类:EmpLog

@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmpLog {private Integer id; //IDprivate LocalDateTime operateTime; //操作时间private String info; //详细信息
}

编写EmpLogMapper接口:

@Mapper
public interface EmpLogMapper {@Insert("insert into emp_log (operate_time, info) values (#{operateTime}, #{info})")public void insert(EmpLog empLog);}

编写EmpLogService接口:

public interface EmpLogService {public void insertLog(EmpLog empLog);}

EmpLogServiceImpl实现类

@Service
public class EmpLogServiceImpl implements EmpLogService {@Autowiredprivate EmpLogMapper empLogMapper;@Overridepublic void insertLog(EmpLog empLog) {empLogMapper.insert(empLog);}
}

在empServiceImpl实现类里面调用:

想一想我们改如何,让一段代码不管怎样都执行了?那就是放在finally代码块里面

在添加完成员工的基本信息后,手动制造一个错误 

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

前后端联调测试:

可以发现报错了。

根据我们上面的猜想,添加日志的操作,不管前面执行成功与否都要执行。

但是数据库里面并没有添加成功,操作日志的记录。

这是为什么呢?我们查看idea的控制台

因为,我们添加员工信息这个实现类里面的方法上,有了一个事务,当我们这个方法里面的出现错误的时候,他要回滚,而添加日志的操作受这个事务的影响,所以我们需要指定,把操作日志记录的,也新建一个事务,使它不受,添加员工信息的影响。

常用的使用场景:

  • REQUIRED :大部分情况下都是用该传播行为即可。
  • REQUIRES_NEW :当我们不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。

 

 在EmpLogServiceImpl实现类里面,添加日志的方法上面。加上@Transaction注解

    @Transactional(propagation = Propagation.REQUIRES_NEW)  //事务传播行为,不管那个方法是否有事务,都会开启新的事务。@Overridepublic void insertLog(EmpLog empLog) {empLogMapper.insert(empLog);}

然后我们在运行,进行前后端联调看效果:

我们发现,程序报错了。看看数据库里面,记录员工操作的日志是否有数据。

可以看到记录日志的操作,在添加员工失败的时候也添加成功了。

分析idea控制台

 在执行分析:

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

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

相关文章

【网络安全】HTTP Slowloris攻击原理解析

文章目录 Slowloris攻击的概念Slowloris攻击原理Slowloris攻击的步骤其他的DDoS攻击类型UDP FloodICMP (Ping) FloodSYN FloodPing of DeathNTP AmplificationHTTP FloodZero-day DDoS 攻击 推荐阅读 Slowloris攻击的概念 Slowloris是在2009年由著名Web安全专家RSnake提出的一…

教育数字化转型 赋能家庭场景自主学习习惯养成

北京市气象台12月12日22时升级发布暴雪橙色预警信号&#xff0c;北京市教委决定自12月13日开始&#xff0c;全市中小学幼儿园采取学生临时居家学习措施。自疫情以来&#xff0c;家庭已经成为另一个学习中心&#xff0c;学校不再是教育的孤岛。 学习方式的变革&#xff0c;数字…

Etcd实战(二)-k8s集群中Etcd数据存储

1 介绍 k8s中所有对象的manifest都需要保存到某个地方&#xff0c;这样他们的manifest在api server重启和失败的时候才不会丢失&#xff0c;因此引入了etcd。在k8s中只有api server和etcd直接交互&#xff0c;其它组件都通过api server间接和etcd交互&#xff0c;这样做的好处…

目标检测锚框

目标检测锚框 最开始呢&#xff0c;我们需要先介绍一下框&#xff0c;先学会一下怎么画框 导入所需要的包 from PIL import Image import d2lzh_pytorch as d2l import numpy as np import math import torch展示一下本次实验我们用到的图像&#xff0c;猫狗 d2l.set_figsiz…

python自动化测试实战 —— WebDriver API的使用

软件测试专栏 感兴趣可看&#xff1a;软件测试专栏 自动化测试学习部分源码 python自动化测试相关知识&#xff1a; 【如何学习Python自动化测试】—— 自动化测试环境搭建 【如何学习python自动化测试】—— 浏览器驱动的安装 以及 如何更…

Linux shell编程学习笔记35:seq

0 前言 在使用 for 循环语句时&#xff0c;我们经常使用到序列。比如&#xff1a; for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i * 2 $(expr $i \* 2)"; done 其中的 1 2 3 4 5 6 7 8 9 10;就是一个整数序列 。 为了方便我们使用数字序列&#xff0c;Linux提供了…

理解Socket

前言 我在去年就学习过Java中Socket的使用&#xff0c;但对于Socket的理解一直都是迷迷糊糊的。看了网上很多关于Socket的介绍&#xff0c;看完还是不太理解到底什么是Socket&#xff0c;还是很迷。直到最近在学习计算机网络&#xff0c;我才对Socket有了一个更深地理解。之前一…

5. PyTorch——数据处理模块

1.数据加载 在PyTorch中&#xff0c;数据加载可通过自定义的数据集对象。数据集对象被抽象为Dataset类&#xff0c;实现自定义的数据集需要继承Dataset&#xff0c;并实现两个Python魔法方法&#xff1a; __getitem__&#xff1a;返回一条数据&#xff0c;或一个样本。obj[in…

uniapp框架——初始化vue3项目(搭建ai项目第一步)

文章目录 ⭐前言&#x1f496; 小程序系列文章 ⭐uniapp创建项目&#x1f496; 初始化项目&#x1f496; uni实例生命周期&#x1f496; 组件生命周期&#x1f496; 页面调用&#x1f496; 页面通讯&#x1f496; 路由 ⭐搭建首页⭐form表单校验页面⭐总结⭐结束 ⭐前言 大家好…

以pycharm为例,生成Python项目所需要的依赖库/包文档:requirements.txt

平时我们在编写或者使用别人的Python项目时&#xff0c;往往会看到一个文档requirements.txt&#xff0c;该文档是描述一个Python项目中的第三方库的名称以及版本。本文介绍导出python当前项目依赖包requirements.txt的操作步骤。 方法一&#xff1a;如果每个项目有对应的虚拟…

【SpringBoot】配置文件

配置文件官网 1. 配置方式 application.propertiesapplication.yml / application.yaml 2. 自定义配置信息 将实体类中的本应该写死的信息写在属性配置文件中。 可以使用 Value("${键名}") 获取&#xff0c;也可以使用 ConfigurationProperties(prefix"前…

java SSM酒店客房管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM酒店客房管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

LAMP平台——构建PHP运行环境

在构建LAMP平台时&#xff0c;各组件的安装顺序依次为Linux、Apache、MySQL、PHP。其中Apache和 MySQL的安装并没有严格的顺序&#xff1b;而PHP环境的安装一般放到最后&#xff0c;负责沟通Web服务器和数据库 系统以协同工作。 PHP 即 Hypertext Preprocessor&#xff08;超级…

python 爬虫 m3u8 视频文件 加密解密 整合mp4

文章目录 一、完整代码二、视频分析1. 认识m3u8文件2. 获取密钥&#xff0c;构建解密器3. 下载ts文件4. 合并ts文件为mp4 三、总结 一、完整代码 完整代码如下&#xff1a; import requests from multiprocessing import Pool import re import os from tqdm import tqdm fro…

深度探索Linux操作系统 —— 构建根文件系统

系列文章目录 深度探索Linux操作系统 —— 编译过程分析 深度探索Linux操作系统 —— 构建工具链 深度探索Linux操作系统 —— 构建内核 深度探索Linux操作系统 —— 构建initramfs 深度探索Linux操作系统 —— 从内核空间到用户空间 深度探索Linux操作系统 —— 构建根文件系统…

媒体直播平台有哪些,活动直播如何扩大曝光?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体直播平台包括人民视频、新华社现场云、中国网、新浪新闻直播、搜狐视频直播、凤凰新闻直播、腾讯新闻直播等。活动直播想要扩大曝光&#xff0c;可以考虑以下方式&#xff1a; 1.选择…

海思平台isp之ccm标定

文章目录 1、raw图采集2、ccm标定2.1、标定参数配置2.2、标定效果优化2.2.1、优化方式一2.2.2、优化方式二2.2.3、优化方式三1、raw图采集 raw图采集步骤及标准,请参考文章 《海思平台isp之ccm标定》。2、ccm标定 2.1、标定参数配置 (1)图像基本参数 (2)黑电平设置 (…

spring boot 实现直播聊天室

spring boot 实现直播聊天室 技术方案: spring bootwebsocketrabbitmq 使用 rabbitmq 提高系统吞吐量 引入依赖 <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.42&…

十六、YARN和MapReduce配置

1、部署前提 &#xff08;1&#xff09;配置前提 已经配置好Hadoop集群。 配置内容&#xff1a; &#xff08;2&#xff09;部署说明 &#xff08;3&#xff09;集群规划 2、修改配置文件 MapReduce &#xff08;1&#xff09;修改mapred-env.sh配置文件 export JAVA_HOM…

从零开始:前端架构师的基础建设和架构设计之路

文章目录 一、引言二、前端架构师的职责三、基础建设四、架构设计思想五、总结《前端架构师&#xff1a;基础建设与架构设计思想》编辑推荐内容简介作者简介目录获取方式 一、引言 在现代软件开发中&#xff0c;前端开发已经成为了一个不可或缺的部分。随着互联网的普及和移动…