Spring事务(声明式事务)(Spring的事务,Spring隔离级别,事务传播机制)

目录

一、什么是事务,为什么要用事务

二、Spring声明式事务

🍅 1、@Transactional的使用

🎈 事务回滚

🎈注意:异常被捕获,不会发生事务回滚

🍅 2、@Transactional 作⽤范围

🍅 3、@Transactional 参数说明 

🍅 4、@Transactional的工作原理

 三、事务的特性以及隔离级别(重要)

🍅 事务的特性(ACID)

🍅 Spring隔离级别

四、Spring事务的传播机制

🍅为什么需要事务传播机制

🍅 事务传播机制的种类

        🎈Propagation.REQUIRED:

        🎈 Propagation.SUPPORTS

         🎈 Propagation.NEVER

        🎈Propagation.NESTED


一、什么是事务,为什么要用事务

定义:事务就是将一组操作封装成一个执行单元(封装到一起),要么全部成功,要么全部失败

为什么要用事务?举例如下:

        转账分为两步:

                第一步:A账户:-100¥

                第二部:B账户:+100¥

        如果没有事务,那么,第一步执行成功,第二部失败,就会导致100¥不见了,不知道转哪里去了。但是当有了事务,着两步操作要么一起成功要么一起失败,这样就可以解决这个问题

分类:Spring中的事务操作分为两类:

  1. 编程式事务(手动写代码操作事务,不常用,复杂)
  2. 声明式事务(利用注解自动开启和提交事务,常用,简便)

下面主要介绍声明式事务

二、Spring声明式事务

🍅 1、@Transactional的使用

🎈 事务回滚

 声明式事务的使用很简单,只需要在方法上添加@Transactional注解就可以实现了

不需要手动开启事务和提交事务,进入方法时自动开启事务,方法执行完就会自动提交事务,如果中途发生了没有处理的异常就会自动回滚事务。

代码示例:

@Data
public class UserInfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private int state;
}
@Mapper
public interface UserMapper {
    @Insert("insert into userinfo(username,password) values (#{username},#{password})")
    int add(UserInfo userInfo);
}
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public int add(UserInfo userInfo){
        return userMapper.add(userInfo);
    }
}
@RequestMapping("/user")
@RestController
public class UserController {

    @Autowired
    private UserService userService;
    @RequestMapping("/add")
    @Transactional
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("huhu");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        System.out.println("result:" + result);
        int num = 10/0;
//        3.将结果给前端
        return result;
    }
}

执行以上代码:结果如下,发生算数异常

 但是sql执行没有异常:

这时候我们查看以下数据库有没有该数据,有没有进行数据回滚?

图中并没有我添加的数据,由此可见,@Transactional注解进行了数据回滚。

🎈注意:异常被捕获,不会发生事务回滚

问题:当自己把程序捕获以后,代表着事务不会发现程序发生了异常,在这种情况下,事务不会发生回滚。

  @RequestMapping("/add")
    @Transactional
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("huhu");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        System.out.println("result:" + result);
        try{
            int num = 10/0;
        }catch (Exception e){

        }
//        3.将结果给前端
        return result;
    }

解决方案1: 将异常继续抛出去(代理对象就能感知到异常,也能够回滚到事务)

@RequestMapping("/add")
    @Transactional
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("万叶");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        System.out.println("result:" + result);
        try{
            int num = 10/0;
        }catch (Exception e){
            throw e;
        }
//        3.将结果给前端
        return result;
    }

新增的信息不在里面:万叶的信息

 解决方案2:使用代码手动回滚事务

@RequestMapping("/add")
    @Transactional
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("万叶");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        System.out.println("result:" + result);
        try{
            int num = 10/0;
        }catch (Exception e){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
//        3.将结果给前端
        return result;
    }


🍅 2、@Transactional 作⽤范围

@Transactional 可以用来修饰类或者方法(public)

  • 修饰方法时,只能应用到public方法上面,否则不生效
  • 修饰类时,表明该注解对该类的所有public方法都生效

🍅 3、@Transactional 参数说明 

一般情况下,以下参数都是默认的


🍅 4、@Transactional的工作原理

@Transactional是基于AOP实现的,AOP又是基于动态代理实现的。

如果目标对象实现了接口,默认情况下就会采用JDK的动态代理,如果目标对象没有实现接口,会使用CGLIB的动态代理。

@Transactional在开始执行业务之前,通过代理先开始事务,在执行成功之后再提交事务。如果中途遇见异常,则回滚事务。

@Transactional实现思路预览:

@Transactional具体执行细节如下图所示:

 


 三、事务的特性以及隔离级别(重要)

🎄 脏读:一个事务读取到了另一个事务修改的数据以后,后一个事务又进行了回滚操作,从而导致第一个事务读取到的数据是错误的。

🎄 不可重复读:一个事务两次查询得到的结果不同,因为在两次查询中间,有另一个事务把数据修改了

🎄 幻读:一个事务两次查询中得到的结果集不同,因为在两次查询中另一个事务又新增了一部分数据。

🍅 事务的特性(ACID)

🎄 原子性(不可分割性)(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束中间某个环节。事务在执行过程中发生错误,就会回滚到事务开始前的状态,相当这个事务从来没有执行过

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

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

🎄 隔离性(Durability):数据库允许多个并发事务同时对齐数据进行读写和修改的能力,隔离性可以放置多个事务并发执行时由于交叉执行导致数据的不一致。事务的隔离分为不同级别,包括读未提交,读提交,可重复读,串行化。

🍅 Spring隔离级别

Spring事务隔离级别有五种:

🎄 Isolation.DEFAULT:以连接的数据库的事务隔离级别为主

🎄 Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读

🎄 Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复度。

🎄 Isolation.REPEATABLE_READ:可重复读,解决了不可重复度,但是存在幻读。

🎄 Isolation.SERIALIZABLE:串行化,可以解决所有并发问题,但是性能太低。

默认情况下,是以SQL的事务隔离级别为主的(Isolation.DEFAULT)。

但是当Sping设置了事务隔离级别以后,就会以Spring的事务隔离级别为主。以下就是以SERIALIZABLE为主的。

 @RequestMapping("/add")
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public int add(){
}

事务的隔离级别保证了多个并发事务执行的可控性。


四、Spring事务的传播机制

定义:事务的传播机制定义了多个包含事务的方法,相互调用时,事务是如何在这些方法间进行传递的。(规定多个事务在相互调用时,事务的执行行为)

🍅为什么需要事务传播机制

事务传播机制是保证一个事务在多个调用方法间的可控性的(稳定性)

事务的传播机制解决的是一个事务在多个节点(方法)中传递的问题:


🍅 事务传播机制的种类

🎄 Propagation.REQUIRED:默认的事务传播级别,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务

🎄 Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

🎄 Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

🎄 Propagation.REQUIRES_NEW:表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立互不干扰。

🎄 Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起

🎄 Propagation.NEVER:以非事务方式运行,如果当前存在事务,则抛出异常

🎄 Propagation.NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于Propagation.REQUIRED

         


        🎈Propagation.REQUIRED:

默认的事务传播级别,表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务

 示例:

UserService:

@Transactional
    public int add(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("add result -> "+result);
        insert(userInfo);
        return result;
    }
    
    @Transactional
    public int insert(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("insert resullt -> "+ result);
        int num = 10 / 0;
        return result;
    }

Controller:

@RequestMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED)
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("万叶");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        return result;
    }

此时结果是,报错是算数异常,进行了事务回滚,数据库中没有添加任何数据


        🎈 Propagation.SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

示例:

UserService

@Transactional(propagation = Propagation.SUPPORTS)
    public int add(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("add result -> "+result);
        insert(userInfo);
        return result;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    public int insert(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("insert resullt -> "+ result);
        int num = 10 / 0;
        return result;
    }

 UserController:

@Transactional(propagation = Propagation.SUPPORTS)
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("万叶");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        return result;
    }

当前调用链不存在事务,结果是数据库中添加了两条数据,并且报错是算数异常,但是并没有进行数据回滚


         🎈 Propagation.NEVER

以非事务方式运行,如果当前存在事务,则抛出异常

 UserController:

 @RequestMapping("/add")
//调用链存在事务
    @Transactional(propagation = Propagation.REQUIRED)
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("万叶");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        return result;
    }

 USerService:

@Transactional(propagation = Propagation.NEVER)
    public int add(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("add result -> "+result);
        insert(userInfo);
        return result;
    }

    @Transactional(propagation = Propagation.NEVER)
    public int insert(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("insert resullt -> "+ result);
        int num = 10 / 0;
        return result;
    }

 说明程序在发现有事务以后,就没有运行了,不存在事务回滚,而是发现事务以后,程序就没有运行,而是直接抛出异常。


        🎈Propagation.NESTED

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于Propagation.REQUIRED

 示例:

UserService:

注意:为什么要try-catch

如果不try-catch就会报错,报错就会使得整个程序进行回滚,这里为了使得满足NESTED的情况,就进行了try-catch,就能使得回滚到事务保存点。

@Transactional(propagation = Propagation.NESTED)
    public int add(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("add result -> "+result);
        return result;

    }

    @Transactional(propagation = Propagation.NESTED)
    public int insert(UserInfo userInfo){
        int result = userMapper.add(userInfo);
        System.out.println("insert resullt -> "+ result);

        try{
            int num = 10 / 0;
        }catch (Exception e){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

UserController:

@RequestMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED)
    public int add(){
//        1.非空判断
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("影");
        userInfo.setPassword("123");
//        2.调用service执行添加
        int result = userService.add(userInfo);
        userService.insert(userInfo);
        return result;
    }

结果如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

跨隔离网文件交换,IT部门和业务部门难以兼顾怎么办?

网络隔离技术作为有效的网络安全和数据安全的管理手段,现在已经被充分运用在企业网络建设中。但企业进行网络隔离是基于安全考虑,被隔离的网络间的数据交换需求不会因网络隔离而消失,因此,企业就需要进行隔离网间的数据和文件交换…

element表格+表单+表单验证结合运用

目录​​​​​​​ 一、结果展示 二、实现代码 一、结果展示 1、图片 2、描述 table中放form表单,放输入框或下拉框或多选框等; 点击添加按钮,首先验证表单,如果存在没填的就验证提醒,都填了就向下添加一行表单表…

力扣:54. 螺旋矩阵(Python3)

题目: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 来源:力扣(LeetCode) 链接:力扣 示例: 示例 1: 输入:matrix [[1,…

过滤器和拦截器的六大区别

平时觉得简单的知识点,但通常都不会太关注细节,一旦被别人问起来,反倒说不出个所以然来。真的就是一看就会一说就废。下面带大家一起结合实践来区分过滤器和拦截器吧~ 通俗理解: (1)过滤器(Fil…

vue-cli

vue-cli脚手架 案例一: 案例二: 案例三: ​ 一、脚手架简介 Vue脚手架是Vue官方提供的标准化开发工具(开发平台),它提供命令行和UI界面,方便创建vue工程、配置第三方依赖、编译vue工程 1. …

2023年华数杯数学建模C题思路 - 母亲身心健康对婴儿成长的影响

# 1 赛题 C 题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一,她不仅为婴儿提供营养物质和身体保护, 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况,如抑郁、焦虑、 压力等,可能会对婴儿的认知、情…

Centos更换网卡名称为eth0

Centos更换网卡名称为eth0 已安装好系统后需要修改网卡名称为eth0 编辑配置文件将ens33信息替换为eth0,可在vim命令模式输入%s/ens33/eth0/g替换相关内容 修改内核文件,添加内容:net.ifnames=0 biosdevname=0 [root@nova3 ~]# vim /etc/default/grub 使用命令重新生成g…

VLE基于预训练文本和图像编码器的图像-文本多模态理解模型:支持视觉问答、图文匹配、图片分类、常识推理等

项目设计集合(人工智能方向):助力新人快速实战掌握技能、自主完成项目设计升级,提升自身的硬实力(不仅限NLP、知识图谱、计算机视觉等领域):汇总有意义的项目设计集合,助力新人快速实…

OBS视频视频人物实时扣图方法(四种方式)

图片擦除一些杂乱图像 参考:https://www.bilibili.com/video/BV1va411G7be https://github.com/Sanster/lama-cleaner第一种:色度键选项 第二种:浏览器建立窗口选项 参考视频:https://www.bilibili.com/video/BV1WS4y1C7QY http…

git报错:Error merging: refusing to merge unrelated histories

碰对了情人,相思一辈子。 打命令:git pull origin master --allow-unrelated-histories 然后等一会 再push 切记不要有冲突的代码 需要改掉~

Redis BigKey案例

面试题: 阿里广告平台,海量数据里查询某一固定前缀的key小红书,你如何生产上限制keys*/flushdb/flushall等危险命令以防止误删误用?美团,MEMORY USAGE命令你用过吗?BigKey问题,多大算big&#…

密码攻击与ADSelfService Plus的保护

密码攻击是当前网络安全面临的严峻挑战之一。黑客通过不断演进的技术手段,试图入侵用户账户,窃取敏感信息,从而对个人和组织造成严重损害。为了应对密码攻击的威胁,ManageEngine推出了ADSelfService Plus,这是一款功能…

Clion开发Stm32之存储模块(W25Q64)驱动编写

前言 涵盖之前文章: Clion开发STM32之HAL库SPI封装(基础库) W25Q64驱动 头文件 #ifndef F1XX_TEMPLATE_MODULE_W25Q64_H #define F1XX_TEMPLATE_MODULE_W25Q64_H#include "sys_core.h" /* Private typedef ---------------------------------------------------…

国联易安网页防篡改保护系统“渠道招募”启动啦!

作为业内专注于保密与非密领域的分级保护、等级保护、业务连续性安全和大数据安全的领军企业,国联易安网页防篡改保护系统基于“高效同步”、“安全传输”两项技术,具备了独特的“五重防护”新特性,支持网页的全自动发布、网页监控、报警和自…

Mybatis-plus动态条件查询QueryWrapper的使用

Mybatis-plus动态条件查询QueryWrapper的使用 一:queryWrapper介绍 queryWrapper是mybatis plus中实现查询的对象封装操作类,可以封装sql对象,包括where条件,order by排序,select哪些字段等等,他的层级关…

Navicat远程连接Linux的MySQL

打开Linux终端,进入root权限,用vim打开MySQL的配置文件 vim /etc/mysql/mysql.conf.d/mysqld.cnf将bind-address的值改为0.0.0.0 进入MySQL mysql -u root -p 将root用户改为允许远程登录 update user set host % where user root; 创建用户 CRE…

uni-app:实现点击按钮出现底部弹窗(uni.showActionSheet+自定义)

一、通过uni.showActionSheet实现底部选择 效果 代码 <template><view><button click"showActionsheet">点击打开弹窗</button></view> </template><script> export default {methods: {showActionsheet() {uni.showAct…

从0开始自学网络安全(黑客)

前言 黑客技能是一项非常复杂和专业的技能&#xff0c;需要广泛的计算机知识和网络安全知识。你可以参考下面一些学习步骤&#xff0c;系统自学网络安全。 在学习之前&#xff0c;要给自己定一个目标或者思考一下要达到一个什么样的水平&#xff0c;是学完找工作&#xff08;…

51单片机学习--DS18B20温度读取温度报警器

需要先编写OneWire模块&#xff0c;再在DS18B20模块中调用OneWire模块的函数 先根据原理图做好端口的声明&#xff1a; sbit OneWire_DQ P3^7;接下来像之前一样把时序结构用代码模拟出来&#xff1a; unsigned char OneWire_Init(void) {unsigned char i;unsigned char Ac…

CCF-CSP 29次 第三题【202303-3 LDAP】(多个STL+递归)

计算机软件能力认证考试系统 #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <unordered_map> #include <string>using namespace std;typedef long long LL;const int N 2510, M 510;int n…