当前位置: 首页 > news >正文

Spring事务传播机制与隔离级别详解

文章目录

  • 1. Spring中事务的实现
    • 1.1 声明式事务@Transactional
  • 2. @Transactional详解
    • 2.1 rollbackFor
    • 2.2. 事务隔离级别
  • 3. 事务传播机制
    • 3.1 事务传播机制有哪些
    • 3.2 Spring事务传播机制的使用
      • 3.2.1 REQUIRED(加入事务)
      • 3.2.2. REQUIRES_NEW(新建事务)
      • 3.2.3 NESTED和REQUIRED 有什么区别?

1. Spring中事务的实现

事务是一组操作的集合, 是一个不可分割的操作
事务会把所有的操作作为一个整体, 一起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成功, 要么同时失败.

事务的操作主要有三步:

  1. 开启事务start transaction (一组操作前开启事务)
  2. 提交事务: commit (这组操作全部成功, 提交事务)
  3. 回滚事务: rollback (这组操作中间任何一个操作出现异常, 回滚事务)

Spring中的事务操作分两类:

  1. 编程式事务(手动写代码操作事务)
  2. 声明式事务(利用注解自动开启和提交事务)

编程式事务感兴趣可自行了解, 本文主要介绍声明式事务

1.1 声明式事务@Transactional

@RequestMapping("/trans")@RestControllerpublicclassTransactionalController{@AutowiredprivateUserServiceuserService;@Transactional@RequestMapping("/registry")Stringregistry(Stringname,Stringpassword){//用户注册userService.registryUser(name,password);inta=10/0;// 强制抛出异常return"注册成功";}}

此时数据库却没有新增数据, 事务进行了回滚

我们一般会在业务逻辑层当中来控制事务, 因为在业务逻辑层当中, 一个业务功能可能会包含多个数据访问的操作. 在业务逻辑层来控制事务, 我们就可以将多个数据访问操作控制在一个事务范围内, 这里写在Controller只是方便演示

@Transactional可以用来修饰方法, 也能修饰类
修饰方法时: 只有修饰public 方法时才生效(修饰其他方法时不会报错, 也不生效)[推荐]
修饰类时:对 @Transactional 修饰的类中所有的 public 方法都生效

方法/类被 @Transactional 注解修饰时, 在目标方法执行开始之前, 会自动开启事务, 方法执行结束之后, 自动提交事务;
如果在方法执行过程中, 出现异常, 且异常未被捕获, 就进行事务回滚操作;
如果异常被程序捕获, 方法就被认为是成功执行, 依然会提交事务, 不会回滚;

我们也可以手动回滚事务, 使用TransactionAspectSupport.currentTransactionStatus() 得到当前的事务, 并使用 setRollbackOnly, 可以写在try-catch中

try{//强制程序抛出异常inta=10/0;}catch(Exceptione){// 手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}

2. @Transactional详解

我们学习了 @Transactional 的基本使用. 接下来我们学习 @Transactional注解的使用细节.

我们主要学习 @Transactional 注解当中的三个常见属性:

  1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型
  2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT
  3. propagation: 事务的传播机制. 默认值为 Propagation.REQUIRED

2.1 rollbackFor

@Transactional 默认只在遇到运行时异常和Error时才会回滚, 非运行时异常不回滚. 即Exception的子类中, 除了RuntimeException及其子类

我们修改下代码(IOException不属于RuntimeException)

@Transactional@RequestMapping("/r2")publicStringr2(Stringname,Stringpassword)throwsIOException{//用户注册userService.registryUser(name,password);log.info("用户数据插入成功");if(true){thrownewIOException();}return"r2";}

此时虽然程序抛出了异常, 但是事务仍然进行了提交, 数据库中任然插入成功

如果我们需要所有异常都回滚, 需要来配置 @Transactional 注解当中的 rollbackFor 属性, 通过 rollbackFor 这个属性指定出现何种异常类型时事务进行回滚

@Transactional(rollbackFor=Exception.class)

2.2. 事务隔离级别

先回顾下MySQL的事务隔离级别:

  1. 读未提交(READ UNCOMMITTED):也叫未提交读. 该隔离级别的事务可以看到其他事务中未提交的数据;

因为其他事务未提交的数据可能会发生回滚, 但是该隔离级别却可以读到, 我们把该级别读到的数据称之为脏数据, 这个问题称为脏数;

  1. 读已提交(READ COMMITTED): 读已提交, 也叫提交读. 该隔离级别的事务能读取到已经提交事务的数据;

该隔离级别不会有脏读的问题.但由于在事务的执行中可以读取到其他事务提交的结果, 所以在不同时间的相同 SQL 查询可能会得到不同的结果, 这种现象叫做不可重复读;

  1. 可重复读(REPEATABLE READ): 事务不会读到其他事务对已有数据的修改, 即使其他事务已提交. 也就可以确保同一事务多次查询的结果一致, 但是其他事务新插入的数据, 是可以感知到的. 这也就引发了幻读问题. 可重复读 是 MySQL 的默认事务隔离级别;

此级别的事务正在执行时, 另一个事务成功的插入了某条数据, 但因为它每次查询的结果都是一样的, 所以会导致查询不到这条数据, 自己重复插入时又失败(因为唯一约束的原因). 明明在事务中查询不到这条信息,但自己就是插入不进去, 这个现象叫幻读

  1. 串行化(SERIALIZABLE): 序列化, 事务最高隔离级别. 它会强制事务排序, 使之不会发生冲突, 从而解决了脏读, 不可重复读和幻读问题, 但因为执行效率低, 所以真正使用的场景并不多
事务隔离级别脏读不可重复读幻读
读未提交
读已提交×
可重复读××
串行化×××

Spring的事务隔离机制有五种:

  1. Isolation.DEFAULT: 以连接的数据库的事务隔离级别为主.
  2. Isolation.READ_UNCOMMITTED: 读未提交, 对应SQL标准中 READ UNCOMMITTED
  3. Isolation.READ_COMMITTED: 读已提交,对应SQL标准中 READ COMMITTED
  4. Isolation.REPEATABLE_READ: 可重复读, 对应SQL标准中 REPEATABLE READ
  5. Isolation.SERIALIZABLE: 串行化, 对应SQL标准中 SERIALIZABLE

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置

@Transactional(isolation=Isolation.READ_COMMITTED)

3. 事务传播机制

事务传播机制: 多个事务方法存在调用关系时, 事务是如何在这些方法间进行传播的.
比如有两个方法A, B都被 @Transactional 修饰, A方法调用B方法
A方法运行时, 会开启一个事务. 当A调用B时, B方法本身也有事务, 此时B方法运行时, 是加入A的事务, 还是创建一个新的事务呢?

事务隔离级别解决的是多个事务同时调用一个数据库的问题;
事务传播机制解决的是一个事务在多个节点(方法)中传递的问题

3.1 事务传播机制有哪些

@Transactional 注解支持事务传播机制的设置, 通过 propagation 属性来指定传播行为

Spring 事务传播机制有以下 7 种:

  1. Propagation.REQUIRED: 默认的事务传播级别. 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则创建一个新的事务;
  2. Propagation.SUPPORTS: 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则以非事务的方式继续运行;
  3. Propagation.MANDATORY: 强制性. 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则抛出异常;
  4. Propagation.REQUIRES_NEW: 创建一个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部方法是否开启事务, Propagation.REQUIRES_NEW 修饰的内部方法都会新开启自己的事务, 且开启的事务相互独立, 互不干扰.
  5. Propagation.NOT_SUPPORTED: 以非事务方式运行, 如果当前存在事务, 则把当前事务挂起(不用).
  6. Propagation.NEVER:以非事务方式运行, 如果当前存在事务, 则抛出异常;
  7. Propagation.NESTED: 如果当前存在事务, 则创建一个事务作为当前事务的嵌套事务来运行.如果当前没有事务, 则该取值等价于 PROPAGATION_REQUIRED

3.2 Spring事务传播机制的使用

对于以上事务传播机制,我们重点关注以下两个就可以了:

  1. REQUIRED(默认值)
  2. REQUIRES_NEW

3.2.1 REQUIRED(加入事务)

以下代码:
用户注册, 插入数据
记录操作日志, 插入数据(出现异常)
观察propagation = Propagation.REQUIRED 执行结果
对应的UserService和LogService操作都添加上了@Transactional(propagation = Propagation.REQUIRED)

@RequestMapping("/propaga")@RestControllerpublicclassPropagationController{@AutowiredprivateUserServiceuserService;@AutowiredprivateLogServicelogService;@Transactional(propagation=Propagation.REQUIRED)@RequestMapping("/p1")publicStringp1(Stringname,Stringpassword){//用户注册userService.registryUser(name,password);//记录操作日志logService.insertLog(name,"用户注册");return"r3";}}
@Slf4j@ServicepublicclassUserService{@AutowiredprivateUserInfoMapperuserInfoMapper;@Transactional(propagation=Propagation.REQUIRED)publicvoidregistryUser(Stringname,Stringpassword){//插入用户信息userInfoMapper.insert(name,password);}}
@Service@Slf4jpublicclassLogService{@AutowiredprivateLogInfoMapperlogInfoMapper;@Transactional(propagation=Propagation.REQUIRED)publicvoidinsertLog(Stringname,Stringop){inta=10/0;//记录用户操作logInfoMapper.insertLog(name,"用户注册");}}

结果:

  1. p1方法开始执行, 开启事务
  2. 用户注册, 插入数据, 执行成功(此操作的事务和p1使用同一个)
  3. 记录操作日志, 插入一条数据, 出现异常, 执行失败(同样和p1 使用同一个事务)
  4. 第三步出现错误, 事务回滚, 步骤2和3使用同一个事务, 所以步骤2的数据也回滚

3.2.2. REQUIRES_NEW(新建事务)

将上述UserService 和LogService 中相关方法事务传播机制改为
Propagation.REQUIRES_NEW

@RequestMapping("/propaga")@RestControllerpublicclassPropagationController{@AutowiredprivateUserServiceuserService;@AutowiredprivateLogServicelogService;@Transactional(propagation=Propagation.REQUIRES_NEW)@RequestMapping("/p1")publicStringp1(Stringname,Stringpassword){//用户注册userService.registryUser(name,password);//记录操作日志logService.insertLog(name,"用户注册");return"r3";}}
@Slf4j@ServicepublicclassUserService{@AutowiredprivateUserInfoMapperuserInfoMapper;@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidregistryUser(Stringname,Stringpassword){//插入用户信息userInfoMapper.insert(name,password);}}
@Service@Slf4jpublicclassLogService{@AutowiredprivateLogInfoMapperlogInfoMapper;@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidinsertLog(Stringname,Stringop){inta=10/0;//记录用户操作logInfoMapper.insertLog(name,"用户注册");}}

运行程序, 发现用户数据插入成功了, 日志表数据插入失败
LogService 方法中的事务不影响 UserService 中的事务
当我们不希望事务之间相互影响时, 可以使用该传播行为

3.2.3 NESTED和REQUIRED 有什么区别?

整个事务如果全部执行成功, 二者的结果是一样的;
如果事务一部分执行成功, REQUIRED加入事务会导致整个事务全部回滚. NESTED嵌套事务可以实现局部回滚, 不会影响上一个方法中执行的结果.

嵌套事务之所以能够实现部分事务的回滚, 是因为事务中有一个保存点(savepoint)的概念, 嵌套事务进入之后相当于新建了一个保存点, 而滚回时只回滚到当前保存点.
REQUIRED 是加入到当前事务中, 并没有创建事务的保存点, 因此出现了回滚就是整个事务回滚, 这就是嵌套事务和加入事务的区别

http://www.jsqmd.com/news/1407514/

相关文章:

  • AgentScope Java Harness:10. Channel Agent 通信的“神经系统“设计
  • 2026年苏州/江苏正规、交付快不锈钢管厂家推荐:不锈钢无缝钢管/三通多通钢管哪家值得看 - 硬核推荐
  • 连带保证、一般保证的担保合同出现追责争议,专业担保合同纠纷律所如何界定保证期间责任 - 好物分享知识传播
  • 遭遇合同诈骗或涉嫌合同诈骗犯罪,专业合同诈骗事务所如何区分民事欺诈与刑事诈骗边界 - 好物分享知识传播
  • 基于人脸关键点与行为时序分析的睡岗识别系统实战指南
  • Overleaf Git同步认证失败排查指南:从HTTPS令牌到SSH密钥的解决方案
  • DM8 事务隔离级别:默认配置 + MySQL/Oracle 行为差异
  • IDEA IntelliJ 2026 版破解
  • 摆脱jnlp束缚,自实现jnlp下载器,jnlpRunner发布
  • AI Agent白手起家77: 从基础认知到多智能体实战的全景回顾
  • 如何根据期刊偏好调整投稿信内容
  • 没有遗嘱的法定遗产分配场景下,专业遗产分配律所如何认定尽赡养义务多分的情形 - 好物分享知识传播
  • Git与Gerrit协同工作流实战:从本地开发到团队代码评审
  • 遭遇家暴起诉离婚,专注家暴离婚案件的律所会优先固定这几类核心证据 - 好物分享知识传播
  • 博科集团IVD全链布局:这家中国企业如何改写体外诊断的“世界规则”?
  • 企业知识库 AI 助手(RAG 为主)产品与技术实现方案
  • 3D 视觉论文精读
  • HarmonyOS HAR开发全攻略:从模块打包到工程化实践
  • Git Push 报错全解析:从权限认证到历史冲突的排查指南
  • 基于YOLOv8的行人检测实战:从环境搭建到RK3588部署全流程
  • 2026年上海GEO代运营服务选型对比全指南 - 筑云鲸
  • 8.16随笔
  • Windows Server防火墙IP拦截实战:从原理到四种配置方法详解
  • 编程训练: 大学计算机 实验3 算法分析设计与应用
  • Gitee开源项目创建与托管全流程指南:从零到协作
  • 侦查、审查起诉、审判不同阶段的刑事案件,专业刑事案件律师事务所分别能提供哪些核心法律帮助 - 好物分享知识传播
  • OSASK学习第3天 进入32位模式并导入C语言
  • likeadmin-api 全驱动数字人参数避坑:file_url、ref_file_url 和 mode 怎么传
  • 农村宅基地流转、继承、翻建遇纠纷,专业宅基地律所处理这类案件的核心法律依据 - 好物分享知识传播
  • LLM as Judge与Best of N:构建自优化的AI代码生成流水线