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

news2024/12/23 4:35:41

事务的四大特性:

概念:

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

 注意:

默认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
     */
    @PostMapping
    public 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的实现类:

    @Override
    public 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
    @Override
    public 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
    @Override
    public 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
    @Override
    public 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.coloradmin.cn/o/1305355.html

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

相关文章

图解transformer中的自注意力机制(备忘)

注意力机制 在整个注意力过程中&#xff0c;模型会学习了三个权重:查询、键和值。查询、键和值的思想来源于信息检索系统。所以我们先理解数据库查询的思想。 假设有一个数据库&#xff0c;里面有所有一些作家和他们的书籍信息。现在我想读一些Rabindranath写的书&#xff1a…

网络编程----select 模型总结

为什么要使用select模型&#xff1f; 答&#xff1a;解决基本C/S模型中&#xff0c;accept()、recv()、send()阻塞的问题 select模型与C/S模型的不同点 C/S模型中accept()会阻塞一直傻等socket来链接select模型只解决accept()傻等的问题&#xff0c;不解决recv(),send()执行…

Android View闪烁动画AlphaAnimation,Kotlin

Android View闪烁动画AlphaAnimation&#xff0c;Kotlin private fun flickerAnimation(view: View?) {val animation: Animation AlphaAnimation(1f, 0f) //不透明到透明。animation.duration 500 // 1次过程时长。animation.interpolator LinearInterpolator() // 线性速…

一天搞定jmeter入门到入职全套教程之Jmeter分布式测试

随着并发量的增大&#xff0c;一台机器就不能满足需求了&#xff0c;所以我们采用分布式&#xff08;Master-Slaver&#xff09;的方案去执行高并发的测试 注意事项&#xff1a; Master机器一般我们不执测试&#xff0c;所以可以拿一台配置差些的机器&#xff0c;主要用来采集…

YOLOv8改进 | 2023主干篇 | 利用RT-DETR特征提取网络PPHGNetV2改进YOLOv8(超级轻量化精度更高)

一、本文介绍 本文给大家带来利用RT-DETR模型主干HGNet去替换YOLOv8的主干&#xff0c;RT-DETR是今年由百度推出的第一款实时的ViT模型&#xff0c;其在实时检测的领域上号称是打败了YOLO系列&#xff0c;其利用两个主干一个是HGNet一个是ResNet&#xff0c;其中HGNet就是我们…

养牛场北斗综合管理系统解决方案

1.系统架构 随着我国北斗卫星导航定位系统的快速发展和定位精度的持续不断提高&#xff0c;在牛身上穿戴定位终端后可以实现对牛的位置和温度的测量&#xff0c;在蜂窝网络正常的情况下&#xff0c;定位和温度数据通过蜂窝网络通信方式回传到监控云平台&#xff0c;在蜂窝网络缺…

使用docker编排容器

使用Dockerfile构建一个自定义的nginx 首先用docker拉一个nginx镜像 docker pull nginx拉取完成后&#xff0c;编辑一个Dockerfile文件 vim Dockerfile命令如下所示,FROM 后面跟的你的基础镜像&#xff0c;而run则是表示你构建镜像时需要执行的指令&#xff0c;下面的指令意…

mysql的ON DELETE CASCADE 和ON DELETE RESTRICT区别

​​ON DELETE CASCADE​​​ 和 ​​ON DELETE RESTRICT​​ 是 MySQL 中两种不同的外键约束级联操作。它们之间的主要区别在于当主表中的记录被删除时,子表中相关记录的处理方式。 ON DELETE CASCADE:当在主表中删除一条记录时,所有与之相关的子表中的匹配记录也会被自动删…

Android studio:打开应用程序闪退的问题2.0

目录 找到问题分析问题解决办法 找到问题 老生常谈&#xff0c;可能这东西真的很常见吧&#xff0c;在之前那篇文章中 linkhttp://t.csdnimg.cn/UJQNb 已经谈到了关于打开Androidstuidio开发的软件后明明没有报错却无法运行&#xff08;具体表现为应用程序闪退的问题&#xff…

MySQL之创建时间类型的字段表

mysql之创建时间类型的字段表 CREATE TABLE tab(birthday DATE, -- 生日job_time DATETIME, -- 记录年月日时分秒login_time TIMESTAMP -- 时间戳NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )解释&#xff1a; NOT NULL DEFAULT &#xff1a;默认不为空…

快速幂(C语言)

前言 快速幂算法一般用于高次幂取模的题目中&#xff0c;比如求3的10000次方对7取模。这时候有些同学会说&#xff1a;这还不简单&#xff1f;我直接调用pow函数然后对结果%7不得了么&#xff1f;可是3的10000次方这么庞大的数字&#xff0c;真的能储存在计算机里么&#xff1f…

Logstash输入Kafka输出Es配置

Logstash介绍 Logstash是一个开源的数据收集引擎&#xff0c;具有实时管道功能。它可以从各种数据源中动态地统一和标准化数据&#xff0c;并将其发送到你选择的目的地。Logstash的早期目标主要是用于收集日志&#xff0c;但现在的功能已经远远超出这个范围。任何事件类型都可…

技术资讯:VSCode大更新,这两个功能终于有了

大家好&#xff0c;我是大澈&#xff01; 本文约1200字&#xff0c;整篇阅读大约需要2分钟。 感谢关注微信公众号&#xff1a;“程序员大澈”&#xff0c;然后免费加入问答群&#xff0c;从此让解决问题的你不再孤单&#xff01; 1. 资讯速览 就在前阵子&#xff0c;前端人都…

【Android嵌入式开发及实训课程实验】【项目1】 图形界面——计算器项目

【项目1】 图形界面——计算器项目 需求分析界面设计实施1、创建项目2、 界面实现实现代码1.activity_main.xml2.Java代码 - MainActivity.java 3、运行测试 注意点结束~ 需求分析 开发一个简单的计算器项目&#xff0c;该程序只能进行加减乘除运算。要求界面美观&#xff0c;…

【Java 基础】27 XML 解析

文章目录 1.SAX 解析器1&#xff09;什么是 SAX2&#xff09;SAX 工作流程初始化实现事件处理类解析 3&#xff09;示例代码 2.DOM 解析器1&#xff09;什么是 DOM2&#xff09;DOM 工作流程初始化解析 XML 文档操作 DOM 树 3&#xff09;示例代码 总结 在项目开发中&#xff0…

电脑ffmpeg.dll丢失如何修复?3个详细修复的教程分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“ffmpeg.dll丢失”。ffmpeg.dll是FFmpeg多媒体框架中的一个重要组件&#xff0c;它负责处理音频和视频的编解码。当这个文件丢失或损坏时&#xff0c;可能会导致一些应用程序无法正常运行。…

iframe 与主应用页面之间如何互相通信传递数据

背景 当我们的Web页面需要复用现有网站的页面时&#xff0c;我们通常会考虑代码层面的抽离引用&#xff0c;但是对于一些过于复杂的页面&#xff0c;通过 iframe 嵌套现有的网站页面也是一种不错的方式&#xff0c;。目前我就职的项目组就有多个业务利用 iframe 完成业务的复用…

【数据结构】堆的模拟实现

前言:前面我们学习了顺序表、单链表、栈、队列&#xff0c;今天我们就开始新的学习吧&#xff0c;今天我们将进入堆的学习&#xff01;(最近博主处于低谷期)一起加油吧各位。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &…

在AWS EC2中部署和使用Apache Superset的方案

大纲 1 Superset部署1.1 启动AWS EC21.2 下载Superset Docker文件1.3 修改Dockerfile1.4 配置管理员1.5 结果展示1.6 检查数据库驱动1.7 常见错误处理 2 Glue&#xff08;可选参考&#xff09;3 IAM与安全组3.1 使用AWS Athena3.2 使用AWS RedShift或AWS RDS3.2.1 查看AWS Reds…

MySQL8.0默认配置详解--持续更新中

binlog日志的默认保留数量和大小 在MySQL 8.0中&#xff0c;您可以使用以下SQL命令来查询binlog日志的默认保留数量和大小&#xff1a; SHOW VARIABLES LIKE binlog_expire_logs_seconds; SHOW VARIABLES LIKE max_binlog_size;binlog_expire_logs_seconds 变量表示binlog日志…