【Spring Boot 事务】万字详解Spring Boot 事务,赶快跟随良辰一起去学习Spring Boot 事务吧! ! !

前言:
大家好,我是良辰丫,这篇文章我将带领大家一起去学习Spring Boot 事务文章,我们在学习数据库的时候已经接触过事务了,来跟随我的脚步一起来瞧一下Spring Boot 事务吧.💌💌💌

🧑个人主页:良辰针不戳
📖所属专栏:javaEE进阶篇之框架学习
🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。
💦期待大家三连,关注,点赞,收藏。
💌作者能力有限,可能也会出错,欢迎大家指正。
💞愿与君为伴,共探Java汪洋大海。

在这里插入图片描述

目录

  • 1. 回忆事务
    • 1.1 事务是什么
    • 1.2 数据库中的事务
  • 2. Spring Boot 事务
    • 2.1 简单回忆一下SSM项目交互过程
    • 2.2 编程式事务
    • 2.3 声明式事务
      • 2.3.1 声明式事务提交
      • 2.3.2 声明式事务回滚
      • 2.3.3 try catch处理异常
  • 3. 注解 @Transactional的参数
  • 4. 注解 @Transactional的工作原理
  • 4. 事务的隔离级别
    • 4.1 事务的四大特性
    • 4.2 设置事务隔离级别的原因
    • 4.3 如何设置事务隔离级别
    • 4.4 数据库的事务隔离级别
    • 4.5 Spring 事务隔离级别

1. 回忆事务

1.1 事务是什么

  • 事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败.举一个简单的转账例子,张三给李四转账100元,此时张三的余额减去100,李四的余额加上100,这样财富和我们的预期.
  • 在我们学习java后端的过程中,事务的学习是必不可少的,因为我们要做项目,我们可以这样理解事务就是处理一些项目中的逻辑问题.

1.2 数据库中的事务

在数据库中我们就接触了事务,在事务中常见的操作如下.

-- 开启事务
start transaction;-- 提交事务
commit;-- 回滚事务
rollback;

2. Spring Boot 事务

  • 今天的学习会依赖咱们的mybatis的学习,如果大家还是对之前知识有疑惑的可以看我的mybatis文章.
  • 链接: MyBatis学习

2.1 简单回忆一下SSM项目交互过程

我们在MyBatis已经带大家了解了SSM项目交互过程,今天我带大家回忆一下,为什么多次说呢?当然因为是核心(重要点),明白了各个层次如何交互,才能够真正去了解SSM代码的架构.

在这里插入图片描述

  • 客户通过浏览器进行访问页面,也就是客户通过浏览器向服务器发送请求.
  • 此时是通过控制器接收请求,收到请求后,控制器调用服务层.
  • 服务层收到控制器的调用请求后,服务层调用持久层.
  • 持久层收到服务层的调用请求后,连接数据(也就是操作数据库,与数据库建立连接).
  • 此时数据库向持久层返回数据,持久层向服务层返回数据,服务器又向控制器返回数据,控制器最终把数据返回给浏览器(也就是前端页面).

2.2 编程式事务

所谓编程式事务,是完全通过代码来实现事务的逻辑,我们只需要简单了解.

  1. 我们先创一个SSM项目框架.

在这里插入图片描述

  1. 在yml配置文件中做相应的配置
# 数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/student?characterEncoding=utf8username: rootpassword: "123456"driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:mapper-locations: classpath:mapper/*Mapper.xmlconfiguration: # 配置打印 MyBatis 执行的 SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL日志级别
logging:level:com:example:demo: debug
  1. 创建实体类
package com.example.demo.entity;import lombok.Data;@Data
public class Stu {private Integer id;private String name;private Integer age;
}
  1. 创建service层
package com.example.demo.service;import com.example.demo.entity.Stu;
import com.example.demo.mapper.StuMapper;
import org.springframework.beans.factory.annotation.Autowired;public class StuService {@Autowiredprivate StuMapper stuMapper;public Integer add(Stu stu){return stuMapper.add(stu);}
}
  1. 创建controller层次
package com.example.demo.controller;import com.example.demo.entity.Stu;
import com.example.demo.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/stu")
public class StuController {@Autowiredprivate StuService stuService;//注入事务的对象@Autowiredprivate DataSourceTransactionManager transactionManager;//设置事务的属性@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/add")public int add(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}//1.开启事务(下面语句表示得到并开启事务)TransactionStatus transactionStatus =transactionManager.getTransaction(transactionDefinition);int res = stuService.add(stu);System.out.println(res);//2.回滚事务transactionManager.rollback(transactionStatus);return res;}
}
  1. 通过postman进行测试

在这里插入图片描述

在这里插入图片描述

接下来我们发现数据库中数据是空的,因为我们进行了回滚操作.

在这里插入图片描述

  1. 提交事务

接下来我们不进行回滚,写一个提交事务的代码.

package com.example.demo.controller;import com.example.demo.entity.Stu;
import com.example.demo.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/stu")
public class StuController {@Autowiredprivate StuService stuService;//注入事务的对象(通过事务管理器拿到事务)@Autowiredprivate DataSourceTransactionManager transactionManager;//设置事务的属性(事务的定义对象,拿一个事务的时候设置相应的属性)@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/add")public int add(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}//1.开启事务(下面语句表示得到并开启事务)TransactionStatus transactionStatus =transactionManager.getTransaction(transactionDefinition);int res = stuService.add(stu);System.out.println(res);//        //2.回滚事务
//        transactionManager.rollback(transactionStatus);//提交事务transactionManager.commit(transactionStatus);return res;}
}

此时我们的数据库就有数据了.

在这里插入图片描述

2.3 声明式事务

  • 编程式事务相当于是手动挡,那么我们的声明式事务就相当于自动挡了(进入智能时代了).
  • 接下来我们需要认识我们的事务注解 @Transactional,它可以自动提交事务.

@Transactional的优点:

  • 可以添加在类上或者方法上,如果添加在类上,那么该类中的所有方法都具有事务的特点.
  • 在方法执行前自动开启事务,在方法执行完(在没有异常的情况下)自动提交事务;当在方法执行期间出现异常的时候就会自动回滚事务.

2.3.1 声明式事务提交

   //声明式的自动提交事务@Transactional@RequestMapping("/add2")public int add2(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}int res = stuService.add(stu);return res;}

可以看出声明式事务几行代码就帮我们搞定了需求,非常方便.

在这里插入图片描述

在这里插入图片描述

接下来我们看一下数据库,我们会发现李四的数据已经在数据库里面了.

在这里插入图片描述

2.3.2 声明式事务回滚

接下来我们制造一个异常,让它自动进行回滚操作.

    //声明式的自动提交事务@Transactional@RequestMapping("/add2")public int add2(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}int res = stuService.add(stu);int num = 100/0;return res;}

此时我们的访问页面就变成了这样,因为前端感知到了异常.

在这里插入图片描述

在这里插入图片描述

显示添加成功,然后我们看数据库

在这里插入图片描述

这里却没有王五的信息,可见回滚成功.
到了这里,大家可能会感到是异常干扰了数据库的执行???
那么我们注释掉提交事务的注解,再次执行观察效果.

在这里插入图片描述

页面还会出现异常信息.

在这里插入图片描述

在这里插入图片描述

因为我们没有加事务的注解,遇到异常不会回滚

2.3.3 try catch处理异常

try catch处理异常后即使有异常也不会进行回滚,因为这样做程序感知不到异常,程序只相信自己感知的异常,如果它感知不到异常,它就认为没有异常.

@Transactional@RequestMapping("/add2")public int add2(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}int res = stuService.add(stu);try {int num = 100/0;} catch (Exception e) {e.printStackTrace();} return res;}

此时我们的浏览器页面并没有报异常.

在这里插入图片描述

然后我们来看我们的数据库信息,我们会发现并没有回滚操作.

在这里插入图片描述
那么我们如何让它在try-catch的处理后还能进行回滚呢?

  1. 将异常抛出,在catch中
   @Transactional@RequestMapping("/add2")public int add2(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}int res = stuService.add(stu);try {int num = 100/0;} catch (Exception e) {throw e;
//            e.printStackTrace();}return res;}

在这里插入图片描述

在这里插入图片描述
但是这种方式并不优雅,有点暴力,浏览器页面也不美观.

  1. 使用代码手动回滚异常
    @Transactional@RequestMapping("/add2")public int add2(Stu stu){//非空校验if (stu == null || stu.getId() ==null|| stu.getName() == null|| stu.getName() == null) {return 0;}int res = stuService.add(stu);try {int num = 100/0;} catch (Exception e) {
//            throw e;
//            e.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return res;}

在这里插入图片描述

在这里插入图片描述

这种方式是比较推荐的.

3. 注解 @Transactional的参数

参数作用
value当配置了多个事务管理器后,可以使用该属性指定选择哪个事务管理器
transactionManager当配置了多个事务管理器后,可以使用该属性指定选择哪个事务管理器
propagation表示事务的传播行为,默认为Propagation.REQUIRED
isolation事务的隔离级别,默认值为Isolation.DEFAULT
timeout事务的超出时间,默认值为-1(没有超出时间),如果超过该时间限制但事务没有完成,那么自动回滚事务(一般不建议大家使用这个)
readOnly指定事务是否为只读事务,默认值为false.为了忽略那些不需要事务的方法,不如读取数据,可以设置readOnly为true.
rollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
rollbackForClassName用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
norollbackFor抛出指定的异常类型,不会滚事务,也可以指定多个异常类型.
norollbackForClassName抛出指定的异常类型,不会滚事务,也可以指定多个异常类型.

4. 注解 @Transactional的工作原理

  • @Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
  • @Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。
  • @Transactional 实现思路如下,其中开启事务与提交事务(或者回滚事务)依赖于切面.
    在这里插入图片描述
  • @Transactional执行过程我们简单了解即可.

4. 事务的隔离级别

4.1 事务的四大特性

  • 原⼦性(Atomicity,或称不可分割性):⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。

  • ⼀致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯作。

  • 持久性(Durability) : 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

  • 隔离性(Isolation,⼜称独⽴性) : 数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化(Serializable)。

4.2 设置事务隔离级别的原因

  • 上面的四种特性中,只有隔离性(隔离级别)是可以设置的
  • 设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操预期的
  • 怎么理解呢,假设有一个平台,张三申请的账号与李四申请的账号资源要进行隔离

4.3 如何设置事务隔离级别

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置

在这里插入图片描述

4.4 数据库的事务隔离级别

  • READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
  • READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
  • REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,⽐如此级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读(Phantom Read)。
  • SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多.
事务隔离级别脏读不可重复读幻读
读未提交
读已提交×
可重复读××
串行化×××
  • 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
  • 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
  • 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。

4.5 Spring 事务隔离级别

  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  • Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  • Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重
    复读。
  • Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级
    别)。
  • Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低

从上述介绍可以看出,相⽐于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个

Isolation.DEFAULT(以数据库的全局事务隔离级别为主)。
Spring 中事务隔离级别只需要设置 @Transactional ⾥的 isolation 属性即可.

@RequestMapping("/stu")
@Transactional(isolation = Isolation.SERIALIZABLE)
public Object save(User user) {// 业务代码
}

后序 :
到了这里关于Spring Boot 事务的学习也就结束了,下一篇文章我将带领大家去学习Spring 事务传播机制.🍬🍬🍬

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

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

相关文章

element框架select值更新页面不回显的问题,动态表单props绑定问题

1、页面中使用form表单&#xff0c;引入select组件 当data中默认没有定义form.region的值时&#xff0c;会出现选择select后input没有回显选择数据值&#xff1b;所以使用select时&#xff0c;必须定义默认值 <el-form ref"form" :model"form" label-…

聊一聊人工智能与视频技术的5大发展趋势与应用

随着互联网的快速发展&#xff0c;视频时代已经到来。据统计&#xff0c;目前互联网内容中&#xff0c;视频内容占据高达82%的流量&#xff0c;未来仍将持续增长。今天我们就来聊一聊关于视频技术的发展&#xff0c;以及现在的大热门–人工智能技术与视频技术的结合。 视频技术…

第38节:cesium 风场效果(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><!

CUDA中的缓存

CUDA缓存包括L1缓存和L2缓存。 SM加载数据&#xff0c;根据不同的设备和类型分为三种路径&#xff1a; 一级和二级缓存常量缓存只读缓存 常规的路径是一级和二级缓存&#xff0c;需要使用常量和只读缓存的需要在代码中显式声明。但是提高性能&#xff0c;主要还是要取决于访问…

茶油生产加工MES质量溯源平台源码(spring boot+mybatis+easyui+mysql+h5)

一、生产加工MES&#xff08;Manufacturing Execution System&#xff0c;简称MES&#xff09;是一种面向车间的生产过程管理与实时信息系统。它主要负责监控生产过程&#xff0c;管理生产资源&#xff0c;优化生产流程&#xff0c;提高生产效率和质量。MES系统需要与ERP系统、…

Android TV:自定义Leanback的VideoDetailsFragment

在Android studio新建TV项目的demo上做修改,实现一下需求: 1、去掉顶部背景区域 2、修改中间详情区域高度 3、修改整体背景界面 效果如图: 搜遍全网,没有找到一个解决方案。只能考自己看代码来自定义实现了。 1、去掉顶部背景区域: VideoDetailsFragment中重写setupD…

【Java项目】拉取公司GitLab项目的教程

文章目录 创建Git账号登录Git 创建Git账号 进入公司后&#xff0c;会拿到公司给你注册的邮箱以及密码&#xff0c;你得到用户名和密码之后&#xff0c;需要先创建一个拉取这个仓库对应的git账号。 我们先登录GitLab 当你登录GitLab之后&#xff0c;会显示你还没有ssh key&…

UE4/5数字人Metahuman与Style3D的使用【二、布料模拟】

目录 鼠标点击布料模拟&#xff1a; 让布料模拟可以跟着动画序列&#xff1a; 有穿模情况&#xff1a; 多件衣服替换&#xff1a; 关卡序列中使用缓存&#xff1a; 效果&#xff1a; UE4/5数字人Metahuman与Style3D的使用【一、Style3DAtelier软件制作smd格式衣服并导入ue】…

VMware虚拟机迁移到阿里云

1. 前言 最近公司内部研发部门有几台jenkins build机器运行在VMware平台上面&#xff0c;由于本地VMware平台底层计算资源不足导致虚拟机运行速度特别慢&#xff0c;每次版本发布都要build好久&#xff0c;而且VMware有时候计算资源不足&#xff0c;还会自动给占用资源大的机器…

使用Docker安装mysql8

Docker中安装mysql8 ​ 本文记录时间为2023-07-04&#xff0c;文档内容主要参照mysql官方文档写成。 一、获取mysql镜像 ​ 此处参考mysql官方的文档&#xff0c;从Oracle的镜像库中查找所需的mysql镜像信息&#xff0c;此处选择的是community-server:8.0 # 拉取myssql com…

Linux12.进程替换

1.进程替换 :将磁盘上新的程序加载到内存中&#xff0c;并和当前进程的页表重新建立映射&#xff0c;这个工作可以调用操作系统的接口完成。 2.execl(函数)&#xff0c;使用该函数后面跟上exit(1)。 3.makefile一次形成多个可执文件 4.chdir函数&#xff0c;作用类似于cd指令。…

LabVIEW开发工业物联网状态监测

物理对象的网络&#xff0c;允许在它们之间传输数据。信息通常保存在集中式云数据库中。由于物联网&#xff0c;我们现在可以从远处进行监控和感知。由于网络和通信的增加&#xff0c;越来越多的流程可能会自动化。 调度、维护管理和质量改进等关键领域的决策正受到大数据技术…