Mybatis引出的一系列问题-Spring事务的探究

news2024/11/19 15:14:41

1 spring事务的传播特性

package com.zs.service;

@Service
public class UserService {
    @Autowired
    private UserDao userDA0;
    @Transactional
    public void transfer(String fromName, String toName, Integer money) {
        userDA0.out(fromName, money);
        int a = 1 / 0;
        userDA0.in(toName, money);
    }
}

在spring中可通过使用注解@Transcation( propagation = “传播机制”)实现事务的传播,Spring中有7种传播机制。所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的,是重新创建事务还是使用父方法的事务?父方法的回滚对子方法的事务是否有影响?这些都是可以通过事务传播机制来决定的。

事务传播行为常量都是以PROPAGATION_ 开头,形如PROPAGATION_XXX。
在这里插入图片描述

1.默认传播:PROPAGATION_REQUIRED

  • 支持当前的事务,如果当前没有事务,就新建事务;
  • 如果当前已有事务,则合并为一个事务。
  • 解释:如果有个父方法A和子方法B,只要有一个带有事务,那么A和B都将拥有事务。

操作1:将BlogServiceImpl和BlogServiceImpl2的事务传播机制都修改为
@Transactional(propagation=Propagation.REQUIRED)
在这里插入图片描述
操作2:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.REQUIRED)

在这里插入图片描述
总结:当BlogServiceImpl提供事务的时候,BlogServiceImpl2的方法执行使用当前已有事务,不再新建事务;当BlogServiceImpl不创建事务的时候,BlogServiceImpl2的方法执行发现没有事务可用,自己新建事务;

2.独立事务:REQUIRES_NEW

  • 如果当前已拥有事务,则把当前事务挂起,新建事务
  • 该机制下的事务不受其它调用者事务的影响
  • 解释:如果有个父方法A(有事务)和子方法B(有事务),如果A抛出异常,而B使用了这个声明事务,那么B仍会继续提交(不受A事务影响)

操作:将BlogServiceImpl事务传播机制为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的为@Transactional(propagation=Propagation.REQUIRES_NEW)

在这里插入图片描述
REQUIRES_NEW为当前方法创建一个新的事务,并且当前事务先提交,然后再提交老的事务

3.NESTED

  • 如果当前存在事务,它将会成为父级的一个子事务,方法结束后并没有提交,只是等待父事务结束才提交。
  • 如果当前没有事务,则新建事务。
  • 如果它本身异常,父级可以捕获到它的异常,而不进行回滚。正常提交。但是如果父级异常,它必然回滚。
  • 解释:一切以父级事务为主

操作1:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.NESTED)
在这里插入图片描述
操作2:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.NESTED)

在这里插入图片描述
总结:save方法创建一个事务,则再调用delete方法时,直接在该事务的基础上创建一个嵌套事务,本质上还是同一个事务,做一次提交;save方法不创建事务,则调用delete方法时,直接创建一个新的事务,单独提交。

4.SUPPORTS

  • 若当前已有事务,则加入事务;
  • 若当前没有事务,则以无事务进行;
  • 解释:佛系事务,有就用,没有就不用了

操作1:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.SUPPORTS)

在这里插入图片描述
操作2:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.SUPPORTS)

在这里插入图片描述
总结: SUPPORTS类型的事务传播机制,是否使用事务取决于调用方法是否有事务,如果有则直接用,如果没有则不使用事务

5.NOT_SUPPORTS

  • 不支持事务,如果当前有事务,则把该事物挂起

操作:将BlogServiceImpl和BlogServiceImpl2的事务传播机制都
修改为@Transactional(propagation=Propagation.NOT_SUPPORTED)
在这里插入图片描述
总结:NOT_SUPPORTED相当于没有Spring事务,每条执行语句单独执行,单独提交

6.MAMDATORY

  • 若当前有事务,则运行当前事务;
  • 若当前没有事务,则抛异常;
  • 解释:父级若没有事务,就不干了

操作:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.MANDATORY),查看是否报错
在这里插入图片描述
总结:MANDATORY必须在已有事务下被调用,否则报错;NOT_SUPPORTED执行数据库层面的事务操作,故当前测试中,insert方法成功执行,delete方法的抛错并不影响insert方法的执行

7.NEVER

  • 有事务就抛异常

操作:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.NEVER),查看是否报错

在这里插入图片描述

2 spring事务的失效

2.1 异常被吃了

 @Transactional(rollbackFor = Throwable.class)
    public void deal() throws FileNotFoundException {
        Bank1 bank1 = bank1Service.getById(1);
        Bank2 bank2 = bank2Service.getById(1);
        //模拟转账
        //用户1转账20给用户B
        bank1.setMoney(bank1.getMoney()-20);
        bank1Service.updateById(bank1);
        try {
            //模拟业务执行出现了异常
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
        bank2.setMoney(bank2.getMoney()+20);
        bank2Service.updateById(bank2);
    }

异常被 try { } catch () { } 包裹了。结果异常没有回滚。原因:异常被方法内部处理了。解决办法:被事务管理的方法有异常直接抛出去,不要用try {} catch(){} 处理。

2.2 错误的标注异常类型

在这里插入图片描述

@Transactional
public void updateProductStockCountById(Integer stockCount, Long id){
 try{
  productDao.updateProductStockCountById(stockCount, id);
 }catch(Exception e){
  throw new Exception("扣减库存异常");
 }
}

在updateProductStockCountById()方法中捕获了异常,并且在异常中抛出了Exception类型的异常,此时,updateProductStockCountById()方法事务的回滚会失效。

为何会失效呢?这是因为Spring中对于默认回滚的事务异常类型为RuntimeException,上述代码抛出的是Exception异常。

默认情况下,Spring事务中无法捕获到Exception异常,所以此时updateProductStockCountById()方法事务的回滚会失效。

此时可以手动指定updateProductStockCountById()方法标注的事务异常类型,如下所示。

@Transactional(rollbackFor = Exception.class)

这里,需要注意的是:Spring事务注解@Transactional中的rollbackFor属性可以指定 Throwable 异常类及其子类。

2.3 方法的事务传播类型不支持事务

如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务在Spring中会失效。

2.4 未配置事务管理器

如果在项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效。例如,没有在项目的配置类中配置如下代码。

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
 return new DataSourceTransactionManager(dataSource);
}

2.5 方法没有被public修饰

如果事务所在的方法没有被public修饰,此时Spring的事务会失效,例如,如下代码所示。

@Service
public class ProductService {
 @Autowired
 private ProductDao productDao;
 
 @Transactional(propagation = Propagation.REQUIRES_NEW)
 private void updateProductStockCountById(Integer stockCount, Long id){
  productDao.updateProductStockCountById(stockCount, id);
 }
}

虽然ProductService上标注了@Service注解,同时updateProductStockCountById()方法上标注了@Transactional(propagation = Propagation.REQUIRES_NEW)注解。

但是,由于updateProductStockCountById()方法为内部的私有方法(使用private修饰),那么此时updateProductStockCountById()方法的事务在Spring中会失效。

2.6 事务方法未被Spring管理

如果事务方法所在的类没有加载到Spring IOC容器中,也就是说,事务方法所在的类没有被Spring管理,则Spring事务会失效,示例如下。

public class ProductService {
 @Autowired
 private ProductDao productDao;
 
 @Transactional
 public void updateProductStockCountById(Integer stockCount, Long id){
  productDao.updateProductStockCountById(stockCount, id);
 }
}

ProductService类上没有标注@Service注解,Product的实例没有加载到Spring IOC容器中,就会造成updateProductStockCountById()方法的事务在Spring中失效。

2.7 数据库不支持事务

Spring事务生效的前提是所连接的数据库要支持事务,如果底层的数据库都不支持事务,则Spring的事务肯定会失效。例如,如果使用的数据库为MySQL,并且选用了MyISAM存储引擎,则Spring的事务就会失效。

2.8 同一个类中非事务管理的方法调用了被事务管理的方法

在这里插入图片描述

如果同一个类中的两个方法分别为A和B,方法A上没有添加事务注解,方法B上添加了 @Transactional事务注解,方法A调用方法B,则方法B的事务会失效。例如,如下代码所示。

@Transactional(rollbackFor = Throwable.class)
public void deal() throws FileNotFoundException {
    Bank1 bank1 = bank1Service.getById(1);
    Bank2 bank2 = bank2Service.getById(1);
    //模拟转账
    //用户1转账20给用户B
    bank1.setMoney(bank1.getMoney() - 20);
    bank1Service.updateById(bank1);
    //模拟业务执行出现了异常
    new FileInputStream(new File("abcd://abcd.text"));
    bank2.setMoney(bank2.getMoney() + 20);
    bank2Service.updateById(bank2);
}

创建一个NoTranMethod类 方法没有加 @Transactional 注解:

@Component
public class NoTranMethod {    
	@Autowired    
	private DealServiceImpl dealService;    
	public void deal() throws FileNotFoundException { 
       dealService.deal();    
    }
}

 @Autowired    
 private NoTranMethod noTranMethod;
 @Test    
 void contextLoads() throws FileNotFoundException { 
     noTranMethod.deal();    
 }

测试 非同一个类中非事务管理的方法调用被事务管理的方法生效了。

又进行了如下测试:

    public void noTranDeal() throws FileNotFoundException {
        deal();
    }
    @Transactional(rollbackFor = Throwable.class)
    public void deal() throws FileNotFoundException {
        Bank1 bank1 = bank1Service.getById(1);
        Bank2 bank2 = bank2Service.getById(1);
        //模拟转账
        //用户1转账20给用户B
        bank1.setMoney(bank1.getMoney() - 20);
        bank1Service.updateById(bank1);
        //模拟业务执行出现了异常
        new FileInputStream(new File("abcd://abcd.text"));
        bank2.setMoney(bank2.getMoney() + 20);
        bank2Service.updateById(bank2);
    }

事务没有生效 什么情况?原因: Spring的声明式事务是基于AOP实现的,而AOP又是基于动态代理,也就说被事务管理的方法将会走代理方法。但是如果是同一个类中非事务管理的方法调用被事务管理的方法是不会调用代理方法的。解决办法:注入代理对象,调用代理对象的方法:

@Service
public class DealServiceImpl {
    @Autowired
    private Bank1Service bank1Service;
    @Autowired
    private Bank2Service bank2Service;
    @Autowired
    private DealServiceImpl dealService;
    public void noTranDeal() throws FileNotFoundException {
        dealService.deal();
    }
    @Transactional(rollbackFor = Throwable.class)
    public void deal() throws FileNotFoundException {
        Bank1 bank1 = bank1Service.getById(1);
        Bank2 bank2 = bank2Service.getById(1);
        //模拟转账
        //用户1转账20给用户B
        bank1.setMoney(bank1.getMoney() - 20);
        bank1Service.updateById(bank1);
        //模拟业务执行出现了异常
        new FileInputStream(new File("abcd://abcd.text"));
        bank2.setMoney(bank2.getMoney() + 20);
        bank2Service.updateById(bank2);
    }
}

注意 自动装配了 本类对象 也就是动态代理生成的代理类。事务生效了:重点: Spring 采用动态代理(AOP)生成代理对象,只有在代理对象之间进行调用时,才可以触发切面逻辑。

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

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

相关文章

在中国人民大学与加拿大女王金融硕士项目的岁月,不会负了每个有心人

学习,就像是一场战争,有时你觉得在这拼命撕杀的战场上,你是孤独与无助的,但你殊不知,你其实并不是孤身奋战的!学习路上会遇见很多有心人,在中国人民大学与加拿大女王金融硕士项目的岁月&#xf…

关于 JavaScript 数组的遍历函数:map, reduce, filter 等

JavaScript 列表的 map 方法是一个常用的数组遍历方法,将会对每一个列表中的元素都应用所给的转换方法,也就是 u > (...),这是 ES6 中匿名函数的表达式写法,在这种使用场景中是一种非常方便的语法糖,map 方法在遍历…

亚马逊鲲鹏系统是怎么引流的?

亚马逊鲲鹏系统有三种引流方式,可设置通过亚马逊站点搜索、站外引流、直接访问产品页面进入到相关产品页面进行操作。 1、通过亚马逊站点搜索 正常的登录到我们的亚马逊主页,然后通过设置关键词及asin,最后进入你指定的产品,进行…

景联文科技高质量成品数据集上新啦!

景联文科技近期上新多个成品数据集,包含图像、视频等多种类型的数据,涵盖丰富的场景,可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型,使得模型能够更加全面和精准地理解和处理任务,更好地应对复…

QT中使用ffmpeg的api进行视频的播放

在了解ffmpeg使用api进行视频的播放之前,我们首先了解一下视频的播放流程。 一、视频的播放流程 首先是我们最常见的视频文件,在播放流程中首先是要打开视频文件,将视频文件中的数据进行解封装,之后再将解封装之后的视频进行解码…

仅需6GB显存,拥有专属AI代码助手

清华GLM技术团队打造的多语言代码生成模型CodeGeeX近期更新了新的开源版本「CodeGeeX2-6B」。CodeGeeX2是多语言代码生成模型CodeGeeX的第二代模型,不同于一代 CodeGeeX ,CodeGeeX2 是基于 ChatGLM2 架构加入代码预训练实现。得益于 ChatGLM2 的更优性能…

Dos常用命令有哪些?具体的使用方式是什么?

1. 常用命令 对我们来说,掌握几个常见的dos命令即可,如下: dir:列出当前目录下所有的文件及文件夹; md:创建一个文件夹; rd:删除一个空目录; cd:进入指定…

第二章 圣诞夜的滑雪场

系列文章目录 第一章 修学旅行(凯撒密码、栅栏密码) 前言 这一章借鉴了基德大人和青子小姐的剧情,感兴趣的童鞋可以看一看哟!(本人柯迷) 摩斯密码 到了下午,老师将大家聚在一起,笑…

云主机OOM宕机原因分析及处理

一、故障现象 某次服务器告警宕机故障,无法ssh连入,控制台登录后查看,发生OOM事件,OOM就是我们常说的Out of Memory内存溢出,它是指需要的内存空间大于系统分配的内存空间,导致项目程序crash,甚…

Ubuntu20.04 + QT5.14.2 + VTK8.2.0 + PCL 1.10 环境配置

目录 Ubuntu20.04 QT5.14.2 VTK8.2.0 PCL 1.10 环境配置一、VTK 编译和安装1、库依赖:2、下载资源:[下载VTK8.2.0](https://www.vtk.org/files/release/8.2/VTK-8.2.0.tar.gz)3、编译:4、安装5、qtcreator 配置编译的libQVTKWidgetPlugin.…

直播录制怎么录?推荐这3个方法!

随着互联网的发展,直播已经成为了一种热门的社交和内容创作方式。然而,有时候我们可能会错过一些重要的直播内容,因此直播录制成为了很多用户的需求。本文将介绍几种直播录制的方法,通过本文的指导,您将学会如何简单易…

微信新功能,你都知道吗?

近日iOS 微信8.0.40正式版来了,一起来看看有哪些变化? 1、朋友圈置顶 几个月前微信开始内测「朋友圈置顶」功能,从网友们的反馈来看,iOS 微信 8.0.40 似乎扩大了内测范围,更多用户可以体验到该功能了。 大家可以去自己…

第一篇|研究数据哪里来——制造业

制造业是一个国家的立国之本,下面为大家介绍一些制造业行业数据的公开信息网站。对于制造业研究数据,您可以从以下几个途径获取: 1. 政府机构和统计局 许多国家和地区的政府机构会定期发布有关制造业的相关数据和统计报告。您可以访问该国或…

零基础挑战一周拿下2023数学建模国奖

1、 数学建模国赛介绍 1.1 数学建模国赛是什么?如何评奖 全国大学生数学建模竞赛是全国高校规模最大的课外科技活动之一。该竞赛每年9月(一般在上旬某个周末的星期五至下周星期一共3天,72小时)举行,竞赛面向全国大专院…

通用积分球的用途和工作原理

积分球辐射源是一种非常优异的定标光源,其输出的辐亮度面均匀性和稳定性是普通光源无法比拟的。在需要使用面光源的领域,被广泛用于光学探测器的实验室定标,空间光学遥感仪器发射前的地面辐射定标。因此辐射源的稳定性、准确性对于辐射定标非…

构建弹性可扩展的微服务架构:基于Spring Cloud Alibaba 的实践

💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 前言 随着互联网业务的…

Netty:ByteBuf可以写入字节数

说明 可以用ByteBuf的maxWritableBytes()得到当前ByteBuf最多还可写入多少字节的数据,它的值等于ByteBuf的最大容量减去当前的writerIndex。 可以使用writableBytes()获得ByteBuf当前还可以写入多少字节的数据,它的值等于ByteBuf的容量减去当前的writer…

连接数据库报错:Bad pocket type 包校验失败

用db连接正式库报错 Bad pocket type 换一个 navicat一样的报错,后来发现是用的数据库不对,沙雕了 重新建立连接,更换成mysql 成功!!!

深度学习(34)—— StarGAN(1)

深度学习(34)—— StarGAN(1) 文章目录 深度学习(34)—— StarGAN(1)1. 背景2. 基本思路3. 整体流程4. StarGAN v2(1) 网络结构(2) mapping network(3) style encoder(4)Loss 和之前…

Spring-1-透彻理解Spring XML的必备知识

学习目标 能够说出Spring的体系结构 能够编写IOC-DI快速入门 思考:为什么学习Spring而不是直接学习SpringBoot 1 Spring介绍 思考:我们为什么要学习Spring框架? 工作上面 Java拥有世界上数量最多的程序员 最多的岗位需求与高额薪资 95%以上服务器端还是要用Jav…