事务的传播机制

article/2025/9/21 2:54:00

目录

1.形象说明:

2.代码演示:

2.1 REQUIRED

2.1.1  验证共用一个事务

2.1.2  验证当前没有事务,就新建一个事务

2.2 SUPPORTS

2.2.1 支持使用当前事务

2.2.2 如果当前事务不存在,则不使用事务

2.3 MANDATORY

2.3.1 支持使用当前事务

2.3.2 如果当前事务不存在,则抛出Exception

2.4 REQUIRES_NEW

2.4.1 当前事务不存在,创建一个新事务

2.4.2 新建一个事务,当前事务存在,把当前事务挂起

2.5 NOT_SUPPORTED

2.5.1 无事务执行

2.5.2 如果当前事务存在,把当前事务挂起。

2.6 NEVER

2.6.1 无事务执行

2.6.2 如果当前有事务则抛出Exception

2.7  NESTED


事务的传播机制有7种,如下图所示:

1.形象说明:

为了更好的理解,下面我们形象的说明一下几种传播机制是什么意思:

比如你下班回家,有以下几种场景

0.REQUIRED ------ 就是如果老婆做饭了,你就吃老婆做的饭;如果老婆没有做饭,你就自己做饭吃,反正你就是要吃饭(反正要在事务中运行);

1.SUPPORTS ------ 如果老婆做饭了,你就吃老婆做的饭;如果老婆没有做饭,你就不吃(不一定非要在事务中运行);

2.MANDATORY ------ 非要吃老婆做的饭,老婆要是没有做饭,你就大发脾气,典型的家暴男;

3.REQUIRES_NEW ------ 劳资非要吃自己做的饭,就算老婆把饭做好了,你也不吃老婆做的;

4.NOT_SUPPORTED ------ 劳资就是不吃饭,就算老婆把饭做好了,我也不吃;

5.NEVER ------ 劳资就是不吃饭,如果老婆把饭做好了,我还要发脾气;

6.NESTED ------ 暂不做解释,后面会详解;

本文主要是想用代码实现这几种传播机制的具体使用;

2.代码演示:

有两张表:

school表

student表

我们如何去测试两个方法是否使用的同一个事务呢?就看是否共用同一个数据库连接或者共用同一个会话;

2.1 REQUIRED

支持使用当前事务,如果当前事务不存在,创建一个新事务。

2.1.1  验证共用一个事务

schoolService.updateSchool()方法:

@Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");//更新id为1的学校名称为湖南大学,地址为湖南schoolMapper.updateByPrimaryKeySelective(school);//调用另一个方法更新学生信息studentService.updateStudent();System.out.println(1/0);}

studentService.updateStudent()方法,加了事务的,默认REQUIRED:

@Transactionalpublic void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);//更新sid为1的学生年龄为25studentMapper.updateByPrimaryKeySelective(student);}

如上所示:updateSchool方法是加了事务的,调用完studentService.updateStudent方法后,会报错,如果updateSchool和updateStudent共用同一个事务,updateSchool报错,自身回滚,肯定会带着updateStudent一起回滚;如果不是共用同一个事务,那么updateStudent会执行成功并提交,不会回滚;

结果:

updateSchool成功回滚了

 updateStudent也成功回滚了

看过我写的深入理解@Transactional注解的使用和原理就知道,两个方法会共用同一个数据库连接,也就共用同一个事务,两个方法一起提交或者回滚;

2.1.2  验证当前没有事务,就新建一个事务

schoolService.updateSchool()方法:

  public void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");//更新id为1的学校名称为湖南大学,地址为湖南schoolMapper.updateByPrimaryKeySelective(school);//调用另一个方法更新学生信息studentService.updateStudent();}

studentService.updateStudent()方法:

@Transactionalpublic void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);//更新sid为1的学生年龄为25studentMapper.updateByPrimaryKeySelective(student);//报错System.out.println(1/0);}

如上所示:updateSchool方法是没有加事务的,调用studentService.updateStudent方法,updateStudent方法加了事务,并且会报错,如果updateStudent没有新建事务的话,不会回滚,如果是建了事务,就会回滚;

结果:

1.控制台提示报错

2. updateSchool方法没有回滚(将北京大学刚改为湖南大学)

 3.updateStudent方法回滚了(将age更新为25)

                                                

验证通过;

2.2 SUPPORTS

支持使用当前事务,如果当前事务不存在,则不使用事务。

2.2.1 支持使用当前事务

updateSchool方法,加事务,更新id为1的name为“湖南大学”,location为“湖南”,输出1/0,肯定会报错的;

    @Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();System.out.println(1/0);}

updateStudent方法更新id为1的age为25;

    @Transactional(propagation = Propagation.SUPPORTS)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);}

如上所示:updateSchool方法是加了事务的,studentService.updateStudent方法也加了事务,而且传播机制是SUPPORTS,如果updateSchool和updateStudent共用同一个事务,updateSchool报错,自身回滚,肯定会带着updateStudent一起回滚;如果不是共用同一个事务,那么updateStudent会执行成功并提交,不会回滚;

结果:

1.控制台提示报错

2. updateSchool方法回滚(将北京大学刚改为湖南大学)

3.updateStudent方法回滚(将age改为25)

显然,updateSchool和updateSchool共用同一个事务,正好验证了传播机制为supports,如果当前有事务,就支持使用当前事务;

2.2.2 如果当前事务不存在,则不使用事务

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,将@Transactional注释掉了,没有加事务

    //@Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为SUPPORTS,打印1/0,将报错

    @Transactional(propagation = Propagation.SUPPORTS)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);System.out.println(1/0);}

如上所示:updateSchool方法没有加事务,studentService.updateStudent方法加了事务,而且传播机制是SUPPORTS,如果updateStudent没有事务,报错就不会回滚,如果有事务,就会回滚;

结果:

1.控制台报错

 2.updateSchool方法没有回滚,无事务执行(无事务这个说法其实不准确,任何操作数据库肯定是有事务的);

 3.updateStudent方法也没有回滚,也是无事务执行的;

显然:  事务传播机制为SUPPORTS的方法,支持使用当前事务,如果当前事务不存在,则不使用事务。

2.3 MANDATORY

中文翻译为强制,支持使用当前事务,如果当前事务不存在,则抛出Exception。

2.3.1 支持使用当前事务

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,打印1/0,会报错;

    @Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();System.out.println(1/0);}

updateStudent方法更新id为1的age为25,事务传播机制为MANDATORY

    @Transactional(propagation = Propagation.MANDATORY)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);}

如上所示:updateSchool方法加事务,studentService.updateStudent方法加了事务,而且传播机制是MANDATORY,如果两者共用一个事务,都会回滚;

结果:

1.控制台报错

2. updateSchool方法回滚(将北京大学刚改为湖南大学)

 3.updateStudent方法回滚(将age改为25)

显然: 传播机制为MANDATORY,如果当前有事务,就使用当前事务;

2.3.2 如果当前事务不存在,则抛出Exception

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,没有事务;

    //@Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为MANDATORY;

    @Transactional(propagation = Propagation.MANDATORY)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);}

如上所示:updateSchool方法没有加事务,studentService.updateStudent方法加了事务,而且传播机制是MANDATORY,就看执行到updateStudent就不会报错;

结果:

1.控制台报错

 org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

2.updateSchool方法没有回滚(将北京大学刚改为湖南大学)

 3.pdateStudent方法直接报错: No existing transaction found for transaction marked with propagation 'mandatory',传播机制为mandatory的,必须有当前事务存在,不存在就报错;

综上:传播机制为MANDATORY,支持使用当前事务,如果当前事务不存在,则抛出Exception。

2.4 REQUIRES_NEW

新建一个新事务;如果当前事务存在,把当前事务挂起。

2.4.1 当前事务不存在,创建一个新事务

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,没有事务;

    //@Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为REQUIRES_NEW,打印1/0,报错;

    @Transactional(propagation = Propagation.MANDATORY)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);System.out.println(1/0);}

如上所示:updateSchool方法没有加事务,studentService.updateStudent方法加了事务,而且传播机制是REQUIRES_NEW,如果updateStudent方法新建了事务,打印1/0,报错就会回滚,如果没有新建事务,updateStudent方法就不会回滚;

结果:

1.控制台报错

2.updateSchool方法没有回滚,无事务执行(无事务这个说法其实不准确,任何操作数据库肯定是有事务的); 

 3. updateStudent方法回滚(将age改为25)

 显然:传播机制为REQUIRES_NEW的,当前没有事务,就新建一个事务,在事务中运行;

2.4.2 新建一个事务,当前事务存在,把当前事务挂起

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,有事务,打印1/0,报错;

    @Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();System.out.println(1/0);}

updateStudent方法更新id为1的age为25,事务传播机制为REQUIRES_NEW;

    @Transactional(propagation = Propagation.REQUIRES_NEW)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);}

如上所示:updateSchool方法有加事务,studentService.updateStudent方法加了事务,而且传播机制是REQUIRES_NEW,如果updateStudent方法新建了另一个事务,updateSchool报错,updateStudent不会回滚,如果不是新建一个事务而是共用一个事务,就会一起回滚;

结果:

1.控制台报错

 2.updateSchool方法回滚(将北京大学刚改为湖南大学)

 3. updateStudent没有回滚(将age改为25)

 显然:updateStudent新建了另外一个事务,和updateSchool并不是共用一个事务;

总结:传播机制是REQUIRES_NEW,新建一个新事务;如果当前事务存在,把当前事务挂起。

使用场景:被调用方法,不想因为调用方出错而回滚,可以使用REQUIRES_NEW;

2.5 NOT_SUPPORTED

无事务执行,如果当前事务存在,把当前事务挂起。

2.5.1 无事务执行

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,没有事务,打印1/0,报错;

    //@Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为NOT_SUPPORTED;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);System.out.println(1/0);}

如上所示:updateSchool方法没有事务,studentService.updateStudent方法加了事务,而且传播机制是NOT_SUPPORTED,如果updateStudent没有事务,那么在执行打印1/0报错后,也就不会回滚;

结果:

1.控制台报错

  2.updateSchool方法不回滚(将北京大学刚改为湖南大学)

  3. updateStudent没有回滚(将age改为25)

 显然:两个方法都没有在事务中运行,都没有回滚,所以,如果当前没有事务,NOT_SUPPORTED并不会新建一个事务,也是无事务执行;

2.5.2 如果当前事务存在,把当前事务挂起。

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,有事务;

    @Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为NOT_SUPPORTED,打印1/0,报错;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);System.out.println(1/0);}

如上所示:updateSchool方法有事务,studentService.updateStudent方法加了事务,而且传播机制是NOT_SUPPORTED,如果updateStudent没有事务,那么在执行打印1/0报错后,也就不会回滚,而updateSchool检测到报错,如果当前事务有效,updateSchool就会回滚;

结果:

1.控制台报错

 2.updateSchool方法回滚(将北京大学刚改为湖南大学)

   3. updateStudent没有回滚(将age改为25)

 显然:当前有事务的情况下,传播机制为NOT_SUPPORTED的方法无事务运行;

总结:事务传播机制为NOT_SUPPORTED,无事务执行,如果当前存在事务,把当前事务挂起;

使用场景:被调用方法想无事务运行,但又不影响调用方的事务,可以用NOT_SUPPORTED;

2.6 NEVER

无事务执行,如果当前有事务则抛出Exception。

这个和MANDATORY就是两个完全相反的极端,一个强制不要事务,一个强制要事务,不满足都会报错;

2.6.1 无事务执行

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,没有事务;

    //@Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为NEVER,打印1/0,报错;

    @Transactional(propagation = Propagation.NEVER)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);System.out.println(1/0);}

如上所示:updateSchool方法没有事务,studentService.updateStudent方法加了事务,而且传播机制是NEVER,打印1/0,会报错,如果updateStudent是无事务执行,那么就不会回滚

结果:

1.控制台报错

2.updateSchool方法不回滚(将北京大学刚改为湖南大学)

 3.updateStudent没有回滚(将age改为25)

  显然:updateSchool和updateStudent都没有回滚,都是无事务执行,所以,传播机制为NEVER,如果当前没有事务,则无事务执行;

2.6.2 如果当前有事务则抛出Exception

updateSchool方法更新id为1的name为“湖南大学”,location为“湖南”,有事务;

    @Transactionalpublic void updateSchool(){School school = new School();school.setId(1);school.setName("湖南大学");school.setLocation("湖南");schoolMapper.updateByPrimaryKeySelective(school);studentService.updateStudent();}

updateStudent方法更新id为1的age为25,事务传播机制为NEVER;

    @Transactional(propagation = Propagation.NEVER)public void updateStudent(){Student student = new Student();student.setSid(1);student.setAge(25);studentMapper.updateByPrimaryKeySelective(student);}

如上所示:updateSchool方法有事务,studentService.updateStudent方法加了事务,而且传播机制是NEVER,当前有事务,就看执行updateStudent方法时到底报不报错;

结果:

1.控制台报错

2.updateSchool方法回滚(将北京大学刚改为湖南大学)

3. updateStudent直接报错

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' 

 显然:传播机制为NEVER,如果当前有事务,则报错;

总结:这种传播机制感觉没啥用途,哈哈,反正我是基本没用过;

2.7  NESTED

嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。

这个直接说,如果父事务回滚,子事务也会跟着回滚;如果子事务回滚,并抛出异常,父事务肯定会跟着回滚;

如果当前没有事务,就和REQUIRED,新建一个事务运行;

关于@Transactional注解的原理和事务传播机制的原理可以看我上一篇文章:

深入理解@Transactional注解的使用和原理


http://chatgpt.dhexx.cn/article/iZ8rDdUK.shtml

相关文章

Spring事务传播机制详解

前言: Spring的事务,也就是数据库的事务操作,符合ACID标准,也具有标准的事务隔离级别。 但是Spring事务有自己的特点,也就是事务传播机制。 所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的&…

反射原理详谈

什么是反射? 反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言…

Java反射的作用与原理

Java反射的作用与原理 定义 反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。在Java中,只要给定类…

彻底搞懂java反射技术及其原理

概述:反射是java中最强大的技术之一,很多高级框架都用到了反射技术,面试中也是经常问的点,所以搞懂反射非常重要! 文章目录 1.反射是什么?2.反射的底层原理3.三种方式获取Class对象4.反射的优缺点5.反射的应用场景6.反射的常用API 1.反射是什么? java反射机制指…

java反射原理-重要

一,反射是什么(反射是框架设计的灵魂) 1,JAVA反射机制是在运行状态中 对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性; …

java 反射机制原理 简述

什么是反射机制? 1、在运行状态中,对于任意一个类,都能够知道这个类的属性和方法。 2、对于任意一个对象,都能够调用它的任何方法和属性。这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射。 反射的作用 1、在运行…

java反射如何实现的_Java反射实现原理

Java反射应用十分广泛,例如spring的核心功能控制反转IOC就是通过反射来实现的,本文主要研究一下发射方法调用的实现方式和反射对性能的影响。 如下为Method类中invoke方法,可以看出该方法实际是将反射方法的调用委派给MethodAccessor&#xf…

Java反射原理与使用

当类加载器将类加载进jvm之后,jvm会创建每一个类的元数据对象(Class),这个元数据对象(Class)记录着这类的所有信息,java语言允许通过元数据对象动态的创建对象实例,这种机制就称为java的反射机制,基本上所有框架的底层都用到了反射机制,spring、mybatis、servlet都用到了 1.如…

Java反射原理简析

Java的反射机制允许我们动态的调用某个对象的方法/构造函数,获取某个对象的属性等,而无需在编码时确定调用的对象。这种机制在我们常用的框架中也非常常见。 1.原理简介 类actionClass Class.forName(“ MyClass”); 对象actio…

java反射原理

一、反射机制 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态(在运行时)获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。简单来说,就是Java对每一个类和类中的所有成…

Java反射(原理剖析与使用)

一、反射机制是什么 1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。 2、Java属于先编译再运行的语言&a…

java反射机制原理详解

Java反射机制是指在运行时动态地获取一个类的信息并能够操作该类的属性和方法的能力。Java反射机制使得程序能够在运行时借助Class类的API来操作自身的属性和方法,从而大大增强了Java的灵活性和可扩展性。本文将详细介绍Java反射机制的原理以及如何使用它。 1、反射…

Java 反射及原理

反射,指的是对于任意一个类,都可以动态的获得它的所有属性和方法,对于任意一个对象都能调用的它的所有属性和方法,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。…

回车、换行、回车换行、硬回车以及软回车

回车、换行、回车换行、硬回车以及软回车 要想一句话说清楚它们之间的关系,不太简单。但认真看完后,会发现它们之间的关系其实也挺简单的。 回车、换行与回车换行 英文缩写对应按键英文全称中文名称解释转义表达式使用平台CRreturnCarriage Return回车…

图解回车和换行的区别

文章目录 1. 定义2. 图解3. 讨论4. 结论 1. 定义 中文英文简写HEXCharacterASCII回车Carriage returnCR0x0D\r13换行Line feedLF0x0A\n10 2. 图解 Win11 Experiment by Pycharm with Python 3.9 print(---)print(Hello World)print(---)# \rprint(Hello \r World)print(---…

不同系统下回车和换行的区别

在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面…

python回车和换行的区别_回车与换行的区别(转)

add by zhj: 不同操作系统下换行符不同,如下: \n: UNIX \n\r: window \r: MAC OS 我们经常遇到的一个问题就是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的…

回车与换行的区别,CRLF、CR、LF详解(\r \n \r\n的区别)

先上结论 缩写ASCⅡ转义系统ASCⅡ值CR\rMacIntosh(早期的Mac)13LF\nUnix/Linux/Mac OS X10CR LF\r\nWindows 很长一段时间里,对于CRLF、CR、LF的理解仅限于不同操作系统下对换行符的定义。所谓知其然需知其所以然,从学习中找到乐…

回车符,换行符的区别

首先介绍一下“回车”(carriage return,’\r’)和“换行”(line feed,’\n’)这两个概念的来历和区别。在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒…

换行和回车的区别

我们在看他们的区别时我们先看看他们的分别指的是什么&#xff1a; 回车<\r>(carriage return)&#xff1a;告诉打印机把打印头定位到左边界&#xff0c;就是指的&#xff0c;那个打印头重新放在这一行的开始。 换行<\n>(line feed)&#xff1a;告诉打印机把打印头…