Spring中的事务与事务传播机制

news2025/1/16 1:44:29
事务

在学习MySQL时我们学习过事务,而到了Spring的学习时,同样会学习到事务,两个东西的事务的定义都是一样的:将一组操作封装成一个执行单元,要么全部成功,要么全部失败

在Spring中使用事务有两种方式

一种是使用编程式事务

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    //编程式事务
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;//事务管理器 -> 所有的事务都是通过这个事务管理器来进行管理的

    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/del")
    public int del(Integer id){

        if(id == null || id < 0){
            return  -1;
        }

        //开启事务
        TransactionStatus transactionStatus =
                dataSourceTransactionManager.getTransaction(transactionDefinition);

        int result = userService.del(id);

        System.out.println("删除: " + result);

//            dataSourceTransactionManager.commit(transactionStatus);//

        dataSourceTransactionManager.rollback(transactionStatus);//为了不污染数据库,所以使用rollback就可以回滚事务

        return result;

    }

}

一种是使用声明式事务

@RestController
@RequestMapping("/user3")
public class UserControllerTransaction {
    @Autowired
    private UserService userService;

    @Transactional
    @RequestMapping("/add")
    public int add(String username,String password){
        if (null == username || null == password || username.equals(" ") || password.equals(" ")){
            return 0;
        }
        User user = new User();
        user.setUsername("wangwu");
        user.setPassword("123456");
        int result = userService.add(user);

        return result;
    }
}

两者的区别在于一种是使用编写大量代码的方式启动事务,另一种是使用注解的方式启动事务

很明显使用注解的方式启动事务的声明式事务更加简单好用,但是使用声明式事务也需要一定的注意:声明式事务只能作用于public修饰的方法上,如果作用在public修饰的类上那么该类的所有public方法都会被添加事务


我们使用声明式事务时,如果方法内出现了异常但是异常被捕获了,那么此时我们的事务不会进行回滚操作

    @RequestMapping("/del")
    @Transactional
    public int del(Integer id){
        if (id == null || id <= 0){
            return 0;
        }
        try{
            int num = 10 / 0;
        }catch (Exception e){
            System.out.println(e);
        }
        return userService.del(id);
    }

可以看到,当我们的代码内部出现了异常但是事务并没有出现回滚操作

原因在于在代码里面自己把异常处理了,所以代码里面不会抛出异常

当Transactional没有检测到异常的出现的时候那么就会认为代码式正常执行的

所以事务不会进行回滚操作

解决方案:1.不要添加try catch或者抛出异常让框架Transactional来感知异常,自动回滚

        try{
            int num = 10 / 0;
        }catch (Exception e){
            throw e;
        }

2.自己手动添加回滚操作

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

事务的四大特性:原子性,隔离性,持久性,一致性,其中Spring事务的隔离性与学习MySQL时遇到的隔离性几乎是完全一样的:

 Spring 中事务隔离级别包含以下 5 种:

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

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

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

4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级 别)

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

可以看到在Spring中只有Isolation.DEFAULT这个隔离级别是特有的,其余隔离级别与MySQL相同


事务传播机制

事务传播机制级别分为七个

1.Propagation.REQUIRED:Spring默认的事务传播级别,如果当前存在事务那么就加入当前事务,如果当前不存在事务,那么就创建一个事务

2.Propagation.SUPPORTS:如果当前存在事务那么就加入当前事务,如果当前不存在事务,那么就以非事务的方式运行

3.Propagation.MANDATORY:如果当前存在事务那么就加入当前事务,如果当前不存在事务,那么就抛出异常

4.Propagation.REQUIRES_NEW:如果当前存在事务那么就挂起当前事务,重新开启自己的事务,也就是说无论当前是否存在事务,都会新开一个事务,开启的事务相互独立互不干扰

5.Propagation.NOT_SUPPORTED:如果当前存在事务那么就挂起当前事务,以非事务的方式进行运行

6.Propagation.NEVER:以非事务的方式进行运行,如果存在事务那么就抛出异常

7.Propagation.NESTED:如果当前存在事务,那么就创建一个事务作为当前事务的嵌套事务进行运行,如果当前没有事务就等价于Propagation.REQUIRED

这里重点说一下Propagation.NESTED,什么叫做嵌套事务呢?说白了就是记录一个保存点,当事务回滚的时候回滚到保存点的位置,使用Propagation.NESTED可以实现部分事务回滚的效果,因此之前的事务是不受影响的

下面使用代码的方式演示部分事务传播机制级别

Propagation.REQUIRED

@RequestMapping("/save")
@Transactional
public Object save(User user) {
    // 插⼊⽤户操作
    userService.add(user);
    // 插⼊⽇志
    logService.add("⽤户插⼊:" + user.getUsername());
    return true;
}

@Service
public class LogService {
    @Autowired
    private LogMapper logMapper;

    @Transactional(propagation = Propagation.REQUIRES)
    public int add(String content){

        int i = 10 / 0;
        Log log  = new Log();
        log.setId(1);
        log.setMessage(content);
        return logMapper.add(log);
    }
}

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional(propagation = Propagation.REQUIRES)
    public int add(User user){
        int result = userMapper.add(user);
        return result;
    }
}

当我们的代码里面有错误时,整个事务都会被回滚 

1.UserService程序执行完成

 2. LogService 保存⽇志程序报错,因为使⽤的是 Controller 中的事务,所以整个事务回滚

 3. 数据库中没有插⼊任何数据,也就是步骤 1 中的⽤户插⼊⽅法也回滚了


  REQUIRES_NEW

@RequestMapping("/save")
@Transactional
public Object save(User user) {
    // 插⼊⽤户操作
    userService.add(user);
    // 插⼊⽇志
    logService.add("⽤户插⼊:" + user.getUsername());
    return true;
}

@Service
public class LogService {
    @Autowired
    private LogMapper logMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int add(String content){

        int i = 10 / 0;
        Log log  = new Log();
        log.setId(1);
        log.setMessage(content);
        return logMapper.add(log);
    }
}

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int add(User user){
        int result = userMapper.add(user);
        return result;
    }
}

执行结果:UserSevice顺利插入数据,LogService插入数据失败,但两者都没有影响UserController的执行


 Propagation.NEVER

@RequestMapping("/save")
@Transactional
public Object save(User user) {
    // 插⼊⽤户操作
    userService.add(user);
    return true;
}


@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional(propagation = Propagation.NEVER)
    public int add(User user){
        int result = userMapper.add(user);
        return result;
    }
}

 程序直接报错,没有插入任何数据


  Propagation.NESTED

@RequestMapping("/save")
@Transactional
public Object save(User user) {
    // 插⼊⽤户操作
    userService.add(user);
    return true;
}

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int add(User user){
        int result = userMapper.add(user);
        // 插⼊⽇志
        logService.add("⽤户插⼊:" + user.getUsername());
        return result;
    }
}

@Service
public class LogService {
    @Autowired
    private LogMapper logMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int add(String content){

        int i = 10 / 0;
        Log log  = new Log();
        log.setId(1);
        log.setMessage(content);
        return logMapper.add(log);
    }
}

执行结果:User和Log都没有插入数据

原因在于:UserController调用了UserService,UserService执行完毕插入数据调用LogService,LogService出现异常,由于是嵌套事务,执行回滚操作,回滚到调用该方法的位置,回滚到UserService,由于代码中出现异常,所以UserService也需要进行回滚到调用该方法的位置,所以两个数据并没有插入

但是如果说LogService出现异常的时候处理了异常,那么此时只有LogService会出现回滚而UserService可以不用回滚

@Service
public class LogService {
    @Autowired
    private LogMapper logMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int add(String content){
        try{
            int num = 10 / 0;
        }catch (Exception e){
           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        Log log  = new Log();
        log.setId(1);
        log.setMessage(content);
        return logMapper.add(log);
    }
}

从实现了部分回滚的操作


Require和Nested两种事务传播机制级别的区别

Require是将当前事务加入到整个事务当中,假如出现了回滚那么整个事务都需要进行回滚

Nested是将当前事务嵌套到整个事务当中,假如出现了回滚那么可以实现部分事务回滚的操作,部分事务回滚到保存点(也就是调用该方法的代码处),不会影响上一个方法的执行结果

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

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

相关文章

GitHub星标超70K,阿里大佬的架构总结“分布式全解”笔记霸榜

分布式架构与微服务平台是当今IT界的关键技术&#xff0c;也是资深软件工程师和系统架构师必须掌握的核心技术。 因此小编为各位粉丝朋友带来这份阿里大佬的分布式笔记&#xff1a;从传统分布式架构迁移到基于容器技术的微服务架构为主线&#xff0c;全面、透彻地介绍了与分布…

十七、Webpack搭建本地服务器

一、为什么要搭建本地服务器&#xff1f; 目前我们开发的代码&#xff0c;为了运行需要有两个操作&#xff1a; 操作一&#xff1a;npm run build&#xff0c;编译相关的代码&#xff1b;操作二&#xff1a;通过live server或者直接通过浏览器&#xff0c;打开index.html代码…

【业务功能篇106】 微服务-springcloud-springboot-电商订单模块--秒杀服务-定时任务【下篇】

四、秒杀活动 1.秒杀活动关注点 秒杀活动的最大特点就是高并发而且是短时间内的高并发&#xff0c;那么对我们的服务要求就非常高&#xff0c;针对这种情况所产生的共性问题&#xff0c;对应的解决方案&#xff1a; 2. 秒杀服务前端 当我们点击 秒杀抢购按钮后&#xff0c;对应…

【C#-1】C#调用matlab生成的dll库

matlab打包dll 1、matlab示例程序&#xff1a; function untitled4(x)z peaks(x);figuresurf(z) end 2、输入deploytool打包matlab程序&#xff0c;具体如下&#xff1a; 3、拷贝 打包成功后&#xff0c;将生成for_redistribution_files_only文件夹中的dll文件拷贝到C#程序…

【设计模式】三、概述分类+单例模式

文章目录 概述设计模式类型 单例模式饿汉式&#xff08;静态常量&#xff09;饿汉式&#xff08;静态代码块&#xff09;懒汉式(线程不安全)懒汉式(线程安全&#xff0c;同步方法)懒汉式(线程安全&#xff0c;同步代码块)双重检查静态内部类枚举单例模式在 JDK 应用的源码分析 …

Llama2-Chinese项目:1-项目介绍和模型推理

Atom-7B与Llama2间的关系&#xff1a;Atom-7B是基于Llama2进行中文预训练的开源大模型。为什么叫原子呢&#xff1f;因为原子生万物&#xff0c;Llama中文社区希望原子大模型未来可以成为构建AI世界的基础单位。目前社区发布了6个模型&#xff0c;如下所示&#xff1a; FlagAl…

无线振动传感器革新:设备维护的新范式

随着工业界的不断发展和技术的进步&#xff0c;设备维护的方法也在不断演变。过去&#xff0c;维护通常是基于固定的时间表和例行的检查进行的&#xff0c;这种方法往往会导致资源的浪费和不必要的停机时间。然而&#xff0c;现在&#xff0c;随着无线振动传感器的革新&#xf…

【C++】内联函数 ⑤ ( 内联函数总结 | 内联函数代码示例 )

文章目录 一、内联函数总结二、内联函数代码示例1、代码示例 - 普通函数执行分析2、代码示例 - 内联函数执行分析3、代码示例 - 宏代码片段执行分析 一、内联函数总结 回顾下 之前的博客中 介绍的 内联函数 : 内联函数编译 : C 编译器 编译 内联函数 时 , 会直接 将 内联函数 …

前端开发纷繁复杂,是否有更高效的开发方式?

一、前言 此前&#xff0c;我曾跟大家聊到&#xff0c;低代码编程&#xff0c;在现阶段互联网业务疯狂增长的带动之下&#xff0c;被赋予了全新的使命和义务&#xff0c;即帮助开发者在前期以较低成本的方式&#xff0c;快速构建一个可投入市场的应用。 那么&#xff0c;有没有…

腾讯云轻量2核4G5M服务器_CPU内存_流量_带宽_系统盘

腾讯云轻量2核4G5M服务器&#xff1a;CPU内存流量带宽系统盘性能测评&#xff1a;轻量应用服务器2核4G5M带宽&#xff0c;免费500GB月流量&#xff0c;60GB系统盘SSD盘&#xff0c;5M带宽下载速度可达640KB/秒&#xff0c;流量超额按照0.8元每GB支付流量费&#xff0c;轻量2核4…

都2023年了,别再用ifconfig啦! 试试这个吧!

下午好&#xff0c;我的网工朋友。 ifconfig这玩意儿&#xff0c;还有多少人在用&#xff1f;举个手看看 总在说ifconfig即将被淘汰&#xff0c;但其实很多网工还在用吧。 比如说组合使用诸如ifconfig、route、arp和netstat等命令行工具&#xff08;来源于安装包net-tools&a…

调查称全球多所顶尖高校网站存在网络攻击风险

Cyber News的一项调查研究显示&#xff0c;全球多所顶尖高校的网站未能及时更新安全补丁&#xff0c;存在敏感信息泄露&#xff0c;甚至被攻击者全面接管的风险。 Cyber​​ News 研究团队详细调查了 20 个每月有数百万访问量的高校网站&#xff0c;其中至少有6个是位于全球Top…

Mac清理垃圾软软件CleanMyMac X的软件功能介绍

在我们使用我们的Mac一定的时间后&#xff0c;总是不可避免的出现Mac内存不足的情况&#xff0c;所以清理垃圾软件也就成为了我们电脑里必不可少的软件。苹果软件商店中有很多各有不同的清理垃圾软件&#xff0c;但我们往往很难从这一大堆软件中找到令自己心满意足的。 但是这…

目录操作函数

mkdir函数 rmdir函数 删除空目录 rename函数 换名 chdir函数 修改当前的工作目录 getcwd函数 获取当前工作的路径

X2-VLM: All-In-One Pre-trained Model For Vision-Language Tasks论文笔记

Title&#xff1a;X2-VLM: All-In-One Pre-trained Model For Vision-Language Tasks Code 1. Motivation CLIP这一类方法只能进行图片级别的视觉和文本对齐&#xff1b;也有一些方法利用预训练的目标检测器进行目标级别的视觉和文本对齐&#xff0c;但是只能编码目标内部的特…

WMS仓储-亿发数字化工厂库存管理系统,提升中小企业仓储管理效率

中小企业竞争日益激烈的市场中&#xff0c;仓库管理的重要性不可低估。为了适应不断变化的业务规模和模式&#xff0c;中小企业对仓库管理提出了更高水平的智能化要求。数字化工厂库存管理系统&#xff0c;正是应运而生&#xff0c;助力企业加强仓库智能作业效率&#xff0c;并…

CRM销售管理系统如何提高销售效率

CRM销售管理系统是帮助企业对销售活动进行管理、执行和优化的软件系统。它可以帮助企业提高销售效率&#xff0c;提高客户转化率&#xff0c;实现企业的业绩增长。那么&#xff0c;CRM销售管理系统好用吗&#xff1f; CRM销售管理系统的功能 线索管理&#xff1a; CRM系统可…

​金蝶云星空管理中心反序列化RCE漏洞复现 附POC

文章目录 ​金蝶云星空管理中心反序列化RCE漏洞复现0x01 前言0x02 漏洞描述0x03 影响范围0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现4.进行ipconfig命令测试 0x06修复建议 ​金蝶云星空管理中心反序列化RCE漏洞复现 0x01 前言 免责声明&#xff1a;请勿利用文…

iframe 实现跨域,两页面之间的通信

一、 背景 一个项目为vue2&#xff0c;一个项目为vue3&#xff0c;两个不同的项目实现iframe嵌入&#xff0c;并实现通信 二、方案 iframe跨域时&#xff0c;iframe组件之间常用的通信&#xff0c;主要是H5的possmessage方法 三、案例代码 父页面-vue2&#xff08;端口号为…

日常开发小汇总(5)元素跟随鼠标移动(在视口下移动)

<div class"mark"><h1>title</h1><div><p>title 鼠标移动 盒子整体移动</p><p>test</p><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Modi, porro.</p></div></div>cons…