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

这篇具有很好参考价值的文章主要介绍了清晰、明了的@Transcation事务嵌套使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、概述

  事务(Transaction):指数据库中执行的一系列操作被视为一个逻辑单元,要么全部成功地执行,要么全部失败回滚,保证数据的一致性和完整性。

  @Transactional注解是Spring框架提供的用于声明事务的注解,作用于类和方法上。

1. @Transactional注解

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

2. Spring事务原理

  Spring的事务是依靠aop实现的。

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

  示意图如下(原始类指加了@Transcation的类):
清晰、明了的@Transcation事务嵌套使用
  如果嵌套调用呢?
  method1方法上有事务注解,method2没有,method1调用了method2。会生成如下的代理类:
清晰、明了的@Transcation事务嵌套使用




二、@Transactional使用

2.1 事务失效的7种情况:

  这里用一个demo举例子:更新一条数据,我们先删除数据,再插入新数据(主键自动递增)。我们希望删除或插入哪一方失败,数据库都能回滚。Spring事务失效是指发生异常依然不回滚。

1. 同一个类中方法调用

  开发中避免不了会对同⼀个类⾥⾯的⽅法调⽤,⽐如有⼀个类 Test,它的⼀个⽅法 A,A 再调⽤本类的⽅法 B(不论⽅法 B 是⽤ public 还是 private 修饰),但⽅法 A 没有声明注解事务,⽽ B ⽅法有。

  外部调⽤⽅法 A 之后,⽅法 B 的事务是不会起作⽤的,这也是经常犯错误的⼀个地⽅。

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

  为什么失效:
  其实这还是由于使⽤ Spring AOP 代理造成的,没加@Transactional注解的方法不会被代理类重写,也就不会有事务。

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

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上回滚。
清晰、明了的@Transcation事务嵌套使用


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

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

	@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的

  事务是依赖AOP实现的,如果方法不能被重写,就不能生产代理类。

   结论:java实现的动态代理的原理是代理类实现被代理类的相同接口、或成为被代理类的子类,然后重写相同方法


6. 数据库不支持事务

  mysql的MyISAM存储引擎是不支持事务的,它是旧版 MySQL(MySQL 5.5 之前)中的默认存储引擎。


7. 设置了某些事务传播行为

  在事务嵌套的时候,设置了以下传播行为,会让事务挂起(相当于没有事务)或抛异常。

  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常
    //外层
    @Transactional
    public void A(Category category) {
        this.categoryDao.insert(category);
        this.B(category.getCid());
    }
	
	//内层
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public boolean B(Integer cid) {
        int i = this.categoryDao.deleteById(cid);
        if(i>0) {
            throw new RuntimeException("xxx"); 
        }
        return true;
    }


2.2 事务6种传播机制

   默认是REQUIERD,保证只有一个事务。

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




总结

  spring相当多的功能都用到了动态代理,还需要对这方面知识做个总结,学无止境啊。文章来源地址https://www.toymoban.com/news/detail-472585.html

到了这里,关于清晰、明了的@Transcation事务嵌套使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Docker部署springcloud项目(清晰明了)

    最近在想做个cloud项目,gitee上找了个模板项目,后端使用到 Nacos、Gateway、Security等技术,需要到 Docker 容器部署 ,在此总结一下,若有不足之处,望大佬们可以指出。 Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等

    2024年02月06日
    浏览(31)
  • VSCode 配置 Lua 开发环境(清晰明了)

    由于 AutoJS 学得已经差不多了,基本都会了,现在开始向其他游戏脚本框架进发, Lua 语言很强大 ,就不多说, 按键精灵、触动精灵等等都是用该语言编程脚本的,由于按键精灵、触动精灵 和 AutoJS 类似,不是说一样是因为按键精灵、触动精灵整合大漠插件等牛逼插件,控制

    2024年02月07日
    浏览(43)
  • WIndows 配置多版本python环境,非常清晰明了

    配置多个python环境 下面以配置三个python版本环境为例子 首先下载好三个环境的python,如: python2.7、python3.6、python3.10 给个官网链接自己下,想要几版本就下几:https://www.python.org/downloads/windows/ 下载完成后将python2.7中的python.exe文件改名为python2.exe,也可以叫python2.7.exe, 反正叫

    2024年02月10日
    浏览(31)
  • MySQL5.7版本在CentOS系统安装 保姆级教程 从小白开始 步骤清晰简单明了

    注意:需要使用root权限 ps:图片大都为安装MySQL8.0版本时的截图仅供参考,实际指令请参照红色字体代码,8.0和5.7版本安装步骤相同,个别地方代码有所差别 安装MySQL8.0版本:http://t.csdn.cn/CSOqM 目录 一. 安装 1. 配置yum仓库 2. 使用yum安装MySQL 3. 安装完成后,启动MySQL并配置开机

    2024年02月08日
    浏览(53)
  • Spring(九) - 解惑 spring 嵌套事务.2

        1. 事务传播特性             在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看,绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义 Java代码  我们可

    2024年02月14日
    浏览(33)
  • Spring中事务嵌套这么用一定得注意了!!

    最近项目上有一个使用事务相对复杂的业务场景报错了。在绝大多数情况下,都是风平浪静,没有问题。其实内在暗流涌动,在有些异常情况下就会报错,这种偶然性的问题很有可能就会在暴露到生产上造成事故,那究竟是怎么回事呢? 我们用一个简单的例子模拟下,大家也

    2023年04月22日
    浏览(26)
  • 关于spring嵌套事务,我发现网上好多热门文章持续性地以讹传讹

    事情起因是,摸鱼的时候在某平台刷到一篇spring事务相关的博文,文章最后贴了一张图。里面关于嵌套事务的表述明显是错误的。 更奇怪的是,这张图有点印象。在必应搜索 PROPAGATION_NESTED 出来的第一篇文章,里面就有这这部份内容,也是结尾部份完全一模一样。 更关

    2023年04月24日
    浏览(34)
  • AopContext.currentProxy的原理和实战(解决同一个类中方法嵌套事务的失效问题)

    @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class TransactionTest {       /**      * 方法A没事务      *      * @param      * @return void      */     public String A(){         System.out.println(\\\"A方法执行开始!!!\\\");         //生成TransactionTest代理类,再调用B方法,B的事

    2024年02月01日
    浏览(67)
  • Sui链上事务处理概述

    Sui通过其混合式交易处理方法,实现比其他区块链更快速和高效的性能。这种方法使得Sui的交易测试吞吐率达到每秒297,000次。从实际应用的角度来看,使用Sui的用户在apps和游戏中几乎能够获得实时响应。 在区块链世界中,交易是apps运作的基础,因为许多应用中的操作会引发

    2024年02月16日
    浏览(30)
  • spring-transaction源码分析(1)概述和事务传播级别

    spring-tx包使用注解驱动和AOP通知将事务开启、提交/回滚、以及复杂的传播机制封装了起来,开发者不再需要编写事务管理的代码,而是可以只关注自己的业务逻辑。 本文将简单介绍spring-tx使用步骤以及七种事务传播级别。 后续文章会阅读源码,深入分析spring-tx aop通知、七种

    2024年02月03日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包