Spring事务操作全解析

张开发
2026/6/7 20:43:41 15 分钟阅读
Spring事务操作全解析
一. 什么是事务1.1 事务的概念事务是一组操作要么全部成功要么全部失败回滚不中间停、不部分生效。---事务的原子性1.2 事务的操作事务的操作主要有三步:1. 开启事start transaction/ begin (⼀组操作前开启事务)2. 提交事务: commit (这组操作全部成功, 提交事务)3. 回滚事务: rollback (这组操作中间任何⼀个操作出现异常,回滚事务)注意提交事务 与 回滚事务 只会操作其中一个-- 开启事务 start transaction; -- 提交事务 commit; -- 回滚事务 rollback;二. Spring 中事务的实现以前我们学习了MySQL中事务的实现现在我们来学习Spring 中事务的实现Spring 中的事务操作分为两类编程式事务(手动写代码操作事务).声明式事务(利用注解自动开启和提交事务).2.1 编程式事务了解即可Spring 手动操作事务和上面 MySQL 操作事务类似, 有3个重要操作步骤• 开启事务(获取事务)• 提交事务• 回滚事务Slf4j RestController RequestMapping(/user) public class UserController { Autowired UserService userService; Autowired DataSourceTransactionManager dataSourceTransactionManager;//JDBC 事务管理器 Autowired TransactionDefinition transactionDefinition;//定义事务属性 RequestMapping(/registry) public String registry(String name,String password){ // 开启事务 TransactionStatus transactionStatus dataSourceTransactionManager .getTransaction(transactionDefinition); //⽤⼾注册 userService.registryUser(name,password); //提交事务 dataSourceTransactionManager.commit(transactionStatus); //回滚事务 //dataSourceTransactionManager.rollback(transactionStatus); return 注册成功; } }注意DataSourceTransactionManager 事务管理器.用来来获取事务(开启事务),提交或回滚事务TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得⼀个事务TransactionStatus编程/手动式事务中事务的回滚与提交都是我们程序员控制的并且事务的回滚与提交是不能同时执行的----这就要我们自己加入条件去控制了测试事务的提交RequestMapping(/registry) public String registry(String name,String password){ // 开启事务 TransactionStatus transactionStatus dataSourceTransactionManager .getTransaction(transactionDefinition); //⽤⼾注册 userService.registryUser(name,password); //提交事务 dataSourceTransactionManager.commit(transactionStatus); return 注册成功;事务提交成功测试事务回滚RequestMapping(/registry) public String registry(String name,String password){ // 开启事务 TransactionStatus transactionStatus dataSourceTransactionManager .getTransaction(transactionDefinition); //⽤⼾注册 userService.registryUser(name,password); //回滚事务 dataSourceTransactionManager.rollback(transactionStatus); return 注册成功;通过查看表我们发现我们注册的信息并未有出现----发生事务回滚我们来看自动递增更加能体现事务的回滚我们现在就知道了在事务回滚前注册是成功了的自动递增从2变为3但是执行到事务回滚的代码后---数据发生回滚注意事务回滚只能撤销对数据的修改Insert/Update/Delete无法撤销自增序列的分配。事务回滚只是针对开启事务的代码块[自己管辖范围内]进行数据的回滚未开启事务的代码依旧是正常执行的事务回滚撤销数据库里的操作比如插入的用户数据、更新的余额事务回滚不会修改 / 撤销 / 回滚任何内存中的变量、方法返回值、打印日志等。2.2 声明式事务 Transactional2.2.1 Transactional的使用使用声明式事务需要我们引入以下依赖dependency groupIdorg.springframework/groupId artifactIdspring-tx/artifactId /dependency然后在需要事务的方法上添加Transactional 注解就可以实现了.无需手动开启事务和提交事 务,进⼊方法时自动开启事务,方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务.Slf4j RestController RequestMapping(/user) public class UserController { Autowired UserService userService; RequestMapping(/registry) Transactional//开启事务 public String registry(String name,String password){ //⽤⼾注册 userService.registryUser(name,password); return 注册成功; } }程序运行插入数据成功修改程序使之出现异常Slf4j RestController RequestMapping(/user) public class UserController { Autowired UserService userService; RequestMapping(/registry) Transactional//开启事务 public String registry(String name,String password){ //⽤⼾注册 userService.registryUser(name,password); int a10/0;//制造异常 return 注册成功; } }调用该方法---事务回滚当我们对异常进行捕获再次运行程序Slf4j RestController RequestMapping(/user) public class UserController { Autowired UserService userService; RequestMapping(/registry) Transactional//开启事务 public String registry(String name,String password){ //⽤⼾注册 userService.registryUser(name,password); try { int a10/0; }catch (Exception e) { log.info(异常捕获成功); } return 注册成功; } }事务提交成功如果我们对异常进行捕获后再次抛出呢再次运行程序Slf4j RestController RequestMapping(/user) public class UserController { Autowired UserService userService; RequestMapping(/registry) Transactional//开启事务 public String registry(String name,String password){ //⽤⼾注册 userService.registryUser(name,password); try { int a10/0; }catch (Exception e) { throw e;//捕获异常后抛出 } return 注册成功; } }程序抛出异常事务回滚从以上四个测试点说明事务是否回滚只看整个方法是否抛出异常只要抛出异常就会发生事务的回滚从前面我们可以了解到如果方法中抛出异常且只要我们进行捕获事务就会提交成功这违背了我们使用事务的初衷所以有在捕获异常的前提下也能回滚事务的方法吗----当然有捕获异常后手动回滚事务----看下面代码Slf4j RestController RequestMapping(/user) public class UserController { Autowired UserService userService; RequestMapping(/registry) Transactional//开启事务 public String registry(String name,String password){ //⽤⼾注册 userService.registryUser(name,password); try { int a10/0; }catch (Exception e) { //TransactionAspectSupport.currentTransactionStatus():获取到当前事务 setRollbackOnly()事务回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return 注册成功; } }2.2.2 Transactional 详解Transactional 源码如下package org.springframework.transaction.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.aot.hint.annotation.Reflective; import org.springframework.core.annotation.AliasFor; Target({ElementType.TYPE, ElementType.METHOD})//既可以加在类上也可以加在方法上 Retention(RetentionPolicy.RUNTIME) Inherited Documented Reflective public interface Transactional { AliasFor(transactionManager) String value() default ; AliasFor(value) String transactionManager() default ; String[] label() default {}; Propagation propagation() default Propagation.REQUIRED; Isolation isolation() default Isolation.DEFAULT; int timeout() default -1; String timeoutString() default ; boolean readOnly() default false; Class? extends Throwable[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class? extends Throwable[] noRollbackFor() default {}; String[] noRollbackForClassName() default {}; }我们主要学习 Transactional 注解当中的三个常见属性:1. rollbackFor: 异常回滚属性.指定能够触发事务回滚的异常类型.可以指定多个异常类型2. Isolation: 事务的隔离级别.默认值为 Isolation.DEFAULT3. propagation: 事务的传播机制.默认值为Propagation.REQUIREDrollbackForTransactional默认只在遇到 运行时异常 和 Error 时才会回滚图中圈出部分, 非运行时异常不回滚.如果我们需要所有异常都回滚,需要来配置 Transactional 注解当中的 rollbackFor 属性,通过 rollbackFor 这个属性指定出现何种异常类型时事务进行回滚Transactional(rollbackFor {Exception.class,Error.class})总结在Spring的事务管理中默认只在遇到运行时异常RuntimeException和Error时才会回滚.如果需要回滚指定类型的异常,可以通过rollbackFor属性来指定.2.3 事务隔离级别记住事务隔离级别存在的前提 多个事务用户同时操作同一张表 / 同一行数据2.3.1 MySQL事务隔离级别Mysql中也有事务的隔离级别在这里我们不过多介绍简单的回顾以下即可隔离级别英文名称脏读不可重复读幻读核心说明适用场景读未提交READ UNCOMMITTED允许允许允许最低级别事务能读到其他事务未提交的数据几乎不用数据一致性极差读已提交READ COMMITTED禁止允许允许事务只能读到其他事务已提交的数据多数数据库Oracle、PostgreSQL默认级别可重复读REPEATABLE READ禁止禁止禁止同一个事务内多次读取同一数据结果一致MySQL 默认级别兼顾一致性与性能串行化SERIALIZABLE禁止禁止禁止最高级别事务串行执行完全无并发对数据一致性要求极高的场景如金融对账脏读读到其他事务未提交的脏数据未提交 还没最终确定后面可能提交也可能回滚撤销不可重复读同一事务内两次读取同一数据结果不一致在你这个事务还没结束的时候别的事务已经把数据改了并且提交成功了。同一事务 一次 begin 到一次 commit 之间的所有操作。幻读同一事务内两次查询的数据行数不一致在你的事务还没有结束时其他事务对数据执行了插入 / 删除操作并且已经提交成功导致你再次查询时结果集的行数发生变化2.3.2 Spring 事务隔离级别Spring 事务隔离级别有五种Spring 枚举值中文名称对应 SQL 标准说明DEFAULT数据库默认—以当前连接数据库的默认事务隔离级别为准READ_UNCOMMITTED读未提交READ UNCOMMITTED允许读取其他事务未提交的数据READ_COMMITTED读已提交READ COMMITTED只能读取其他事务已提交的数据REPEATABLE_READ可重复读REPEATABLE READ同一事务内多次读取同一数据结果一致SERIALIZABLE串行化SERIALIZABLE事务串行执行最高隔离级别注意Spring 不自己实现隔离只是告诉 MySQL 要用什么级别最终执行隔离的是 MySQL 数据库Spring 多一个DEFAULTMySQL 没有DEFAULT这种选项Spring 用DEFAULT来自动适配数据库默认值非常方便只要级别名称对应脏读、不可重复读、幻读 的表现完全相同实际效果完全一样三. Spring事务传播机制3.1 什么是Spring 事务传播机制当一个带有事务的方法调用另一个也带有事务的方法时事务该怎么传递、怎么合并、怎么新建这就是事务传播行为。比如有两个方法A,B都被 Transactional 修饰, A方法调用B方法 A方法运行时,会开启⼀个事务.当A调用B时,B方法本身也有事务,此时B方法运行时,是加入A的事务,还是创建⼀个新的事务呢? 这个就涉及到了事务的传播机制.3.2 事务的传播机制有哪些传播行为枚举值中文说明核心规则Propagation.REQUIRED默认级别当前有事务则加入无事务则新建一个事务Propagation.SUPPORTS支持事务当前有事务则加入无事务则以非事务方式运行Propagation.MANDATORY强制事务当前有事务则加入无事务则直接抛出异常Propagation.REQUIRES_NEW新建独立事务始终创建新事务当前有事务则挂起内外事务独立互不干扰Propagation.NOT_SUPPORTED不支持事务始终以非事务运行当前有事务则将其挂起Propagation.NEVER禁止事务始终以非事务运行当前有事务则抛出异常Propagation.NESTED嵌套事务当前有事务则作为嵌套子事务运行无事务则等价于 REQUIRED3.3 事务的传播机制详细讲解A开启不开启事务B开启事务且在funA中调用了funB那么funB是使用funA的事务还是使用自己的事务----这些均取决与funB设置的事务的传播机制决定Propagation.REQUIRED默认如果A有事务则B和A共用同一个事务不使用B的事务A没有事务则B使用自己的事务此时B事务的回滚/提交不会影响到APropagation.SUPPORTS如果A有事务则B和A共用同一个事务不使用B的事务A没有事务则B以非事务的方式运行B自己的事务不起任何作用Propagation.MANDATORY如果A有事务则B和A共用同一个事务不使用B的事务A没有事务则B直接抛出异常该机制要求A必须要有事务Propagation.REQUIRES_NEW不管 A 有没有事务B都是只是使用自己事务如果 A 有事务就先把 A 的事务挂起等 B 执行完提交后再恢复 A 的事务。A 和 B 事务互不干扰、互不影响、各自回滚。Propagation.NOT_SUPPORTEDB始终以非事务方式运行若A存在事务则将其挂起B绝不参与任何事务。(B的报错不会引起A的事务回滚A的事务回滚也不会影响到B操作数据的变化)Propagation.NEVERB始终以非事务方式运行若A存在事务B直接抛出异常Propagation.NESTED如果A有事务则B使用自己的事务作为嵌套事务B 报错回滚只回滚 B 自己不影响 AA 报错回滚连带着 B 一起回滚若A不存在事务则B使用自己的事务

更多文章