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

1 spring事务的传播特性

package com.zs.service;@Service
public class UserService {@Autowiredprivate UserDao userDA0;@Transactionalpublic 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给用户Bbank1.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 {@Autowiredprivate 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 {@Autowiredprivate ProductDao productDao;@Transactionalpublic 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给用户Bbank1.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给用户Bbank1.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 {@Autowiredprivate Bank1Service bank1Service;@Autowiredprivate Bank2Service bank2Service;@Autowiredprivate 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给用户Bbank1.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.rhkb.cn/news/77112.html

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

相关文章

[CKA]考试之一个 Pod 封装多个容器

由于最新的CKA考试改版,不允许存储书签,本博客致力怎么一步步从官网把答案找到,如何修改把题做对,下面开始我们的 CKA之旅 题目为: Task 创建一个Pod,名字为kucc1,这个Pod包含4容器&#xff…

思科模拟器配置静态路由(下一跳使用IP)

Router0配置代码:##端口配置 Router(config)#int fastEthernet 0/0 Router(config-if)#ip address 192.168.10.254 255.255.255.0 Router(config-if)#no shutdown Router(config-if)#int fastEthernet 0/1 Router(config-if)#ip address 192.168.20.1 255.255.255.2…

pycharm——树状图

from pyecharts import options as opts from pyecharts.charts import Treedata [{"children": [{"name": "计算机"},{"children": [{"children": [{"name": "主机"}], "name": "硬盘…

真机搭建中小网络

这是b站上的一个视频,演示了如何搭建一个典型的中小网络,供企业使用 一、上行端口:上行端口就是连接汇聚或者核心层的口,或者是出广域网互联网的口。也可理解成上传数据的端口。 二、下行端口:连接数据线进行下载的端…

一百四十二、Linux——查看Linux服务器架构的版本类型

一、目的 查看已经安装好的Linux服务器架构的版本类型,看服务器版本是32位还是64位 而且可以区分出是kettle的文件x86或x86_64,x86是32位,而x86_64是64位 注意: 32位的查询结果为i386、i686 64位的查询结果为x86_64 二、Linu…

VBA技术资料1-146

VBA技术资料本周更新较多:单值查找并提示结果;多值查找并提示结果;复制整个数据范围到PowerPoint;更改PowerPoint文本框字体大小;调整PowerPoint图像为整幻灯片;在PowerPoint中添加末尾幻灯片;在…

Spring MVC应用的开发步骤

Spring MVC应用的开发步骤 Spring MVC应用的开发步骤如果以异步方式提交请求利用XML配置文件配置控制器类 Spring MVC应用的开发步骤 下面简单介绍Spring MVC应用的开发步骤。 ① 在web.xml文件中配置核心控制器DispatcherServlet处理所有的HTTP请求。 由于Web应用是基于请求/…

C语言基础知识点一

C语言基础知识点一&#xff1a; 1.数据类型 2.bool类型&#xff1a; 使用bool时时&#xff0c;需要增加<stdbool.h>头文件。 说明&#xff1a;bool 类型只有非零&#xff08;true&#xff09;和零&#xff08;false&#xff09;两种值。 如: if&#xff08;-1&#xf…

【ARM Coresight 系列文章 2.3 - Coresight 寄存器】

文章目录 Coresight 寄存器介绍1.1 ITCTRL&#xff0c;integration mode control register1.2 CLAIM寄存器1.3 DEVAFF(Device Affinity Registers)1.4 LSR and LAR1.5 AUTHSTATUS(Authentication Status Register) Coresight 寄存器介绍 Coresight 对于每个 coresight 组件&am…

EXCEL,vlookup以及数据去重

1&#xff0c;新建一个work表格&#xff0c;将数据copy进来&#xff0c;并做简单处理&#xff0c;让看起来舒服 2&#xff0c;使用vlookup函数查找数据是否在库中 注意:上图中的Table_array A1:C152&#xff0c;这个值要加绝对引用&#xff0c;写成&#xff1a; $A$1:$C$15…

Windows下安装Hive(包安装成功)

Windows下安装Hive Hive与Hadoop的版本选择很关键&#xff0c;千万不能选错&#xff0c;否则各种报错。一、Hive下载1.1、官网下载Hive1.2、网盘下载Hive 二、解压安装包&#xff0c;配置Hive环境变量2.1、环境变量新增&#xff1a;HIVE_HOME2.2、修改Path环境变量&#xff0c;…

flutter开发实战-flutter_spinkit实现多种风格进度指示器

flutter开发实战-flutter_spinkit实现多种风格进度指示器 最近开发过程中flutter_spinkit&#xff0c;这个拥有多种种风格加载指示器 一、flutter_spinkit 引入flutter_spinkit # 多种风格的模糊进度指示器flutter_spinkit: ^5.1.0效果示例 const spinkit SpinKitRotatingC…

【C#学习笔记】类型转换

文章目录 类型转换字符转数字GetNumericValueConvert.ToInt32隐式转换计算 字符串转数字Parse 或 TryParse 方法 字节数组转整数 as&#xff0c;is强制类型转换isas 用户定义的转换 类型转换 我们简单地将值类型分为5种&#xff1a;整数型&#xff0c;浮点型&#xff0c;布尔型…

【Rust】Rust学习

文档&#xff1a;Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门 入门指南 - Rust 程序设计语言 简体中文版 第二章猜猜看游戏 猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com) // 导入库 use std::io; use s…

时间复杂度、空间复杂度实践练习(力扣OJ)

目录 文章目录 前言 题目一&#xff1a;轮转数组 思路一&#xff1a; 思路二&#xff1a; 思路三&#xff1a; 题目二&#xff1a;消失的数字 思路一&#xff1a; 思路二&#xff1a; 思路三&#xff1a; 题目三&#xff1a;移除元素 思路&#xff1a; 总结 前言 想要编写高效的…

烘焙小程序蛋糕店烘焙店源码点心店小程序源码

本系统开发使用JAVA技术栈开发 使用uniapp技术栈 支持微信小程序 &#xff0c;对接打印机&#xff0c;对接第三方同城跑腿平台 用户端使用&#xff1a;uniapp 管理端使用&#xff1a;vueelementui 后台服务使用&#xff1a;springbootjpa

Pandas

系列文章目录 第一章 python数据挖掘基础环境安装和使用 第二章 Matplotlib 第三章 Numpy 文章目录 系列文章目录一、介绍1.1 为什么用Pandas&#xff1f;1.2 核心数据结构1.3 DataFrame1.3.1 结构1.3.2 常用属性1.3.3 常用方法1.3.4 DataFrame索引的设置修改行列索引值重设索…

Eureka 学习笔记5:InstanceRegistry

版本 awsVersion ‘1.11.277’ LeaseManager 接口管理实例的租约信息&#xff0c;提供以下功能&#xff1a; 注册实例取消注册实例实例续约剔除过期实例 public interface LeaseManager<T> {/** 注册实例并续约*/void register(T r, int leaseDuration, boolean isRep…

【数模】主成分分析PCA

主成分分析(Principal Component Analysis,PCA)&#xff0c;是一种降维算法&#xff0c;它能将多个指标转换为少数几个主成分&#xff0c;这些主成分是原始变量的线性组合&#xff0c;且彼此之间互不相关&#xff0c;其能反映出原始数据的大部分信息。使用场景&#xff1a;一般…

java序列化框架全集讲解

一、简介 Java序列化框架是一种用于在Java应用程序中将对象转换为字节流或从字节流反序列化为对象的工具。序列化是将对象的状态转换为字节流的过程&#xff0c;以便可以将其存储在文件中、通过网络传输或在不同的系统之间共享。反序列化是将字节流转换回对象的过程。 Java序列…