清晰、明了的@Transcation事务嵌套使用

news2024/11/25 3:32:33

文章目录

  • 概述
    • @Transcation注解
      • 事务实现原理
  • @Transcation使用
    • 1、事务生效的情况:
      • 1. 外层有事务,内层无事务
        • 结论:外层有事务,内层也会有事务
      • 2. 外层事务(requierd),内层事务(not_supported)
        • 结论:不管内层是什么级别的事务、有没有事务,只以外层事务为准
      • 总结:
    • 2、事务不生效的情况:
      • 1. 外层无事务,内层有事务
        • 结论:内层有事务,不影响外层。
      • 2. 异常被 catch 住,而且没有再次抛出异常
        • 结论:没有异常不会回滚。
      • 3. 抛出RuntimeException或Error以外的异常
        • 结论:使用RollbackFor属性添加要捕获的异常类型
      • 4. 子线程内异常
        • 结论:子线程异常抛给主线程,两者一起回滚。
      • 5. 事务方法是private、static、final的
        • 结论:java实现的动态代理的原理是代理类实现被代理类的相同接口,重写相同方法
      • 6. 数据库不支持事务
  • 总结

概述

  事务(Transaction):指数据库中执行的一系列操作被视为一个逻辑单元,要么全部成功地执行,要么全部失败回滚,保证数据的一致性和完整性。
  @Transactional注解是Spring框架提供的用于声明事务的注解,作用于类和方法上。

@Transcation注解

属性可选值作用
propagationREQUIRED
REQUIRES_NEW
NESTED
NOT_SUPPORTED
SUPPORTS
指定事务的传播行为,默认值为Propagation.REQUIRED
isolationDEFAULT
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE
指定事务的隔离级别,默认值为Isolation.DEFAULT
readOnlytrue
false
指定事务是否为只读事务,默认值为false。如果将其设置为true,表示事务只涉及读取操作
timeout数字(秒)指定事务的超时时间(秒),默认值为TransactionDefinition.TIMEOUT_DEFAULT。如果事务在指定的时间内未完成,将被自动回滚
rollbackFor异常类.Class指定触发事务回滚的异常类型数组,默认为空。当方法抛出指定类型的异常时,事务将回滚
noRollbackFor异常类.Class指定不触发事务回滚的异常类型数组,默认为空。当方法抛出指定类型的异常时,事务将不回滚
rollbackForClassName异常类名与rollbackFor类似,但是使用异常类型的完全限定名字符串来指定触发事务回滚的异常
noRollbackForClassName异常类名与noRollbackFor类似,但是使用异常类型的完全限定名字符串来指定不触发事务回滚的异常

事务实现原理

  Spring的事务是依靠动态代理技术实现的,是在程序运行时给代理对象创建代理类。如果方法或类上添加了@Transcation注解,spring会自动给方法或类创建一个代理类,代理类中包含了开关事务的代码和原始操作。

  示意图如下(原始类指加了@Transcation的类):
在这里插入图片描述




@Transcation使用

1、事务生效的情况:

  这里用一个demo举例子:更新一条数据,我们先删除数据,再插入新数据(主键自动递增)。我们希望删除或插入哪一方失败,数据库都能回滚。

1. 外层有事务,内层无事务

  内外层哪一方有异常,全部都会回滚。

    //外层
    @Transactional
    public void update(Category category) {
        this.categoryDao.delete(category);
        this.insert(category);//无事务,但有异常
    }
    
    //内层
    public void insert(Category category) {
    	this.categoryDao.insert(category);
        int a=2/0;
    }

结论:外层有事务,内层也会有事务


2. 外层事务(requierd),内层事务(not_supported)


事务隔离级别:not_supported

  先说说事务隔离级别为not_supported时,事务会被挂起,等于没有事务,有异常也不会回滚。

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public boolean deleteById(Integer cid) {
        int i = this.categoryDao.deleteById(cid);
        if(i>0) {
            throw new RuntimeException("xxx"); //有异常也不回滚
        }
        return true;
    }

  如果外层事务为required(不写类型默认required):内外层哪一方有异常,全部都会回滚。

    //外层
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateById(Category category) {
        this.categoryDao.insert(category);
        this.deleteById(category.getCid());
    }
	
	//内层
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public boolean deleteById(Integer cid) {
        int i = this.categoryDao.deleteById(cid);
        if(i>0) {
            throw new RuntimeException("xxx"); 
        }
        return true;
    }

结论:不管内层是什么级别的事务、有没有事务,只以外层事务为准


总结:

  为什么外层有事务,内层就有事务?借用下图就能理解,method1方法上有事务注解,method1调用了method2。
  生成的代理类长什么样子呢:用事务“包”住了这两个方法的原始代码。
在这里插入图片描述


2、事务不生效的情况:

1. 外层无事务,内层有事务

  内层有异常会回滚,和外层无关。外层有异常不会回滚。

    //外层
    public void update(Category category) {
        this.delete(category);
        this.categoryDao.insert(category);
        //int a=2/0; //外有异常,内无异常,删除成功,插入失败
    }
	
	//内层
	@Transactional
    void delete(Category category) {
        this.categoryDao.delete(category.getId);
        //int a=2/0; //外无异常,内有异常,删除、插入都失败
    }

结论:内层有事务,不影响外层。


2. 异常被 catch 住,而且没有再次抛出异常

  无论是外层异常还是内层异常,只要捕获以后没有抛出异常,都不会回滚。总的来说没有异常不会回滚。

	//外层
    @Transactional
    public void updateById(Category category) {
        try {
            this.categoryDao.deleteById(category.getCid());
            this.categoryDao.insert(category);
            int a=2/0;
        }catch (java.lang.Exception e){
            System.out.println("updateById异常");
        }
    }
    //外层
    public void update(Category category) {
        this.delete(category);
        this.categoryDao.insert(category);
    }
	
	//内层
	@Transactional
    void delete(Category category) {
    	try{
	        this.categoryDao.delete(category.getId);
	        int a=2/0;
        }catch(Exception e){

		}
    }

解决办法:
  捕获后再次抛出异常。无论是内层、外层,只要重新抛出异常,就可以回滚。

    //外层
    public void update(Category category) {
        this.delete(category);
        this.categoryDao.insert(category);
    }
	
	//内层
	@Transactional
    void delete(Category category) {
    	try{
	        this.categoryDao.delete(category.getId);
	        int a=2/0;
        }catch(Exception e){
			throw new RuntimeException("service层deleteById方法异常");//可以自定义异常
		}
    }

结论:没有异常不会回滚。


3. 抛出RuntimeException或Error以外的异常

  @Transcation有个属性(方法),管理何种异常会引发回滚。
  默认情况下,事务只在RuntimeException和Error上回滚。
在这里插入图片描述


  抛出RuntimeException和Error以外类型的异常,不会回滚。   常见的非运行时异常有:SQLException、IOException、FileNotFoundException、ReflectiveOperationException等等。

在这里插入图片描述

	@Transactional
    void delete(Category category) throws SQLException{
        this.categoryDao.delete(category.getId);
        int a=2/0;
       	throw new SQLException("xxx");  //抛出异常也不会回滚
    }

解决办法:
  使用RollbackFor属 添加 要捕获的异常类型,这样除了RuntimeException和Error类型的异常,遇到Exception以及它的子类的异常,也会发生回滚。

	@Transactional(rollbackFor = Exception.class)
    void delete(Category category) throws SQLException{
        this.categoryDao.delete(category.getId);
        int a=2/0;
       	throw new SQLException("xxx");  //这下就回滚了
    }

结论:使用RollbackFor属性添加要捕获的异常类型


4. 子线程内异常

  删除操作新开一个线程执行,并且执行中发生异常。结果是删除回滚,插入没回滚。
  多线程环境下,内外层是两个事务,事务具有隔离性,事务之间不会互相干扰。

    //外层
    @Transactional(rollbackFor = java.lang.Exception.class)
    public void update(Category category) {
        Thread thread=new Thread(new Runnable() {
           @Override
           public void run() {
                delete(category);//删除
            }
        });
        this.categoryDao.insert(category);//插入
    }
	
	//内层
	@Transactional(rollbackFor = java.lang.Exception.class)
    void delete(Category category) {
        this.categoryDao.delete(category.getId);
        int a=2/0; //异常
    }

解决办法
  使用Thread.UncaughtExceptionHandler接口捕获线程异常,主线程发现了异常,也跟着回滚。
  注:事务还是多个事务

  1. 创建一个实现了Thread.UncaughtExceptionHandler接口的异常处理器类,该类将负责捕获未被捕获的(没加try-catch的)线程异常并进行处理:
public class CustomUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 在此可以处理未被捕获的线程异常
        System.out.println("线程 " + t.getName() + " 发生了异常: " + e.getMessage());
    }
}
  1. 在主线程或创建的子线程中,设置自定义的异常处理器:
    @Transactional(rollbackFor = java.lang.Exception.class)
    public void updateById(Category category){
        Thread.setDefaultUncaughtExceptionHandler(new CustomUncaughtExceptionHandler());//加上这句
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                deleteById(category.getCid());//删除
            }
        });
        thread.start();
        this.categoryDao.insert(category);//插入
    }

结论:子线程异常抛给主线程,两者一起回滚。


5. 事务方法是private、static、final的

  方法不能被重写,就不能生产代理类。

结论:java实现的动态代理的原理是代理类实现被代理类的相同接口,重写相同方法


6. 数据库不支持事务




总结

  spring相当多的功能都用到了动态代理,还需要对这方面知识做个总结,学无止境啊。

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

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

相关文章

HEVC环路后处理核心介绍

介绍 为什么需要环路后处理技术 hevc采用基于快的混合编码框架,方块效应、振铃效应、颜色偏差、图像模糊等失真效应依旧存在,为了降低此类失真影响,需要进行环路滤波技术; 采用的技术 去方块滤波DF,为了降低块效应…

ADC和DAC的工作原理及其区别

ADC和DAC的工作原理及其区别 ADC和DAC都是用于模拟信号与数字信号之间的转换器。 ADC,即模数转换器,是将连续的模拟信号转换为数字信号的电路。其输入为模拟信号,输出为数字信号。ADC的主要组成部分是模拟信号采样模块、模拟信号处理模块、模…

生态系统服务(InVEST模型)土壤保持、水源涵养、氮磷输出、生态保护、生物多样性、碳固

白老师(研究员):长期从事生态系统结构-格局-过程-功能-服务的变化与响应关系等研究工作;重点围绕生物多样性、生态系统服务与价值等,构建生物地球化学模型和评价指标体系,为城市、区域和自然保护区的可持续…

sqlserver中动态sql语句应用

前言 一、使用exec 1.用拼接方法 二、使用sp_executesql 1.用拼接方法 2.传参的方法 总结 前言 例如:列表查询条件不固定,根据前端传过来的参数,这时需要根据查询条件后台动态生成SQL语句 一、使用exec exec适用于字符串拼接的方式&#xf…

mac安装python

接上集,我们已经安装了Homebrew 那么在 macOS 上安装 Python 有多种方法,以下是其中两种常用方法: 1:使用 Homebrew 安装 Python Homebrew 是 macOS 上的包管理器,可以方便地安装和管理各种软件包。如果您已经安装了…

美国E8267C是德(KEYSIGHT) E8267D 20G/44G矢量信号发生器

Agilent E8267C、Keysight E8267D、 PSG 矢量信号发生器,高达 44 GHz ​Keysight E8267D (Agilent) PSG 矢量信号发生器是业界首款 I/Q 调制高达 44 GHz 的集成微波矢量信号发生器。它具有先进的宽带内部基带发生器,能够灵活地播放任意波形或生成复杂的…

在Windows11上模拟运行Linux命令的几种方式

在 Windows 上运行 Linux 命令的软件有很多,以下是其中几个比较常用的: Cygwin Cygwin 是一个为 Windows 提供类 Unix 环境的开源软件,它包含了大量的 Unix 工具和命令,可以在 Windows 上运行 Linux 命令。 安装命令 winget i…

【Java 继承】了解Java类的继承的特点,继承的关系,继承的使用,到底什么是继承?

博主:_LJaXi Or 東方幻想郷 专栏: Java | 从入门到入坟 Java 继承 继承的特点 \ 介绍 ❓特点 ♊ 继承的使用方式 🔞避免重复方法 子类访问父类的成员变量 🈲子类访问父类的成员变量(直接访问)访问父类与子类…

MapReduce实战案例(3)

案例三: MR实战之TOPN(自定义GroupingComparator) 项目准备 需求测试数据 有如下订单数据 订单id商品id成交金额Order_0000001Pdt_01222.8Order_0000001Pdt_0525.8Order_0000002Pdt_03522.8Order_0000002Pdt_04122.4Order_0000002Pdt_05722.4Order_0000003Pdt_01222.8 现在…

6 具有 OCR 功能的顶级 PDF 图像转 Word 转换器

如果您在 PDF 图像中找到一些有用的信息并想转换为 Word 格式以供进一步使用,您将需要一个具有OCR 功能的 PDF 图像转 Word 转换器,该转换器旨在识别 PDF 图像中的文本并将其制作出来可编辑。 将 PDF 图像转换为 Word 并不容易,因为我们需要…

高压放大器工作原理(高压放大器怎么用的)

高压放大器是一种能够将低电平信号放大到足够高的电平,以便用于驱动大功率负载或处理高电压信号的电子设备。它广泛应用于各种电子设备中,例如音频放大器、射频放大器、电力电子设备等。下面我们将详细介绍高压放大器的工作原理以及使用方法。 高压放大器…

一分钟:GTP鼓谱导出转换MIDI格式教程

const loadPromise self.osmd.load("/resource/test");loadPromise.then(function () {self.osmd.render();});作为一名鼓手,我深知鼓谱转换MIDI格式的重要性,但是找了好久,一直没有找到一个好用的工具。 直到我发现了GTP鼓谱转换…

下载YouTube视频的一种方法

文章目录 工具名称下载方法使用方法1.只下载音频2.下载音频转换成mp3(加上-x –audio-format参数)3.下载视频(带音频)ID:22 | EXT:mp4 | 1280*720 下载的数据集:YouCook2 工具名称 yt-dlp 下载…

doxygen使用: 跨平台方式让markdown文件包含另一个文件

文章目录 1. 目的和问题2. 解决思路2.1 FILTER_PATTERNS 选项2.2 基于 Python 的 FILTER_PATTERNS 选项2.3 sledcpp.py 脚本 3. 完整工程3.1 目录结构3.2 hello.h 文件内容3.3 CHANGELOG.md 文件内容3.4 generate_doxyfile.py 文件内容3.5 docs/root.md3.6 docs/changelog.md3.…

Redis 事务详细介绍

事务 注意:Redis单条命令是保证原子性的;但是事务不保证原子性! Redis事务没有隔离级别的概念,所有的命令在事务中,并没有直接被执行,只有发起执行命令时才执行 Redis事务本质:一组命令的集合&…

API接口对接的流程和注意的事项

API接口对接是将两个应用程序或系统连接并进行数据交换的过程。在进行API接口对接时,需要确保两个系统具有相同的协议和格式,并且数据传输过程中不会出现错误或数据丢失。下面是API接口对接的流程和注意事项: 流程: 1.确认数据格…

【多目标优化算法】多目标蚱蜢优化算法(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Linux——进程退出

目录 一.进程退出时有三种选择: 1.1 echo $?命令: 功能: 打印距离现在最近一次执行某进程的退出码 例2代码: 例3: 例4代码: 1.3 进程运行过程中可能会出现的错误种类: 二.总结&#xff…

神经网络学习小记录73——Pytorch CA(Coordinate attention)注意力机制的解析与代码详解

神经网络学习小记录73——Pytorch CA(Coordinate attention)注意力机制的解析与代码详解 学习前言代码下载CA注意力机制的概念与实现注意力机制的应用 学习前言 CA注意力机制是最近提出的一种注意力机制,全面关注特征层的空间信息和通道信息…

Unity随手问题记录(持续更新~~)

目录 1.将摄像机定位到模型实际中心点前边(防止有些模型中心点和实际模型中心位置偏移很大的情况) 2.获取当鼠标在RawImage上时,鼠标位置对应的图像坐标(简单粗暴方式) 3.设置脚本运行顺序 4.当plugins底下出现dll文件识别不到的情况&#xf…