背景
作为一个后端开发,@Transactional一直在用,但总会碰到各种离奇的不生效的情况,每次都是到处查资料解决。就想写一篇文章整理一下,后面遇到就不用到处找了,有新情况也再补充。
@Transactional
声明式事务
spring支持 编程式事务 和 声明式事务 两种。编程式事务也就是用代码手动控制事务的开始、提交或回滚,这样业务代码就变得不纯粹,功能代码和辅助代码杂糅到一起,并且会有许多重复代码。所以我们一般使用声明式事务。声明式事务一般有两种方式,一是基于tx和aop命名空间的xml配置文件,二是使用@Transactional注解。目前使用比较广泛的就是@Transactional注解。
属性
属性 | 类型 | 描述 |
---|---|---|
value、transactionManager | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | “读写”或“只读”事务,默认false读写 |
timeout | int (in seconds granularity) | 事务超时时间设置,默认-1 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
value、transactionManager
指定事务管理器的名称,一般用在多数据源应用的事务处理中,用来明确指定使用哪个数据源的事务管理
propagation:事务传播行为【todo 后面写demo代码跑一下验证】
- Propagation.REQUIRED
- 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
- 【外层事务和内层事务是一个事务,要提交一起提交,要回滚一起回滚】
- Propagation.SUPPORTS
- 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- 【如果有外层事务,那么和Propagation.REQUIRED相同。如果没有外层事务,以非事务的方式运行】
- Propagation.MANDATORY
- 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- 【如果有外层事务,那么和Propagation.REQUIRED相同。如果没有外层事务,抛出异常】
- Propagation.REQUIRES_NEW
- 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- 【外层事务回滚,内部事务正常提交;内部事务回滚,外部事务若对异常进行了捕获,没有再向上抛,则外部事务继续执行】
- Propagation.NOT_SUPPORTED
- 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- 【外层事务不影响,内层以非事务方式运行】
- Propagation.NEVER
- 以非事务方式运行,如果当前存在事务,则抛出异常。
- Propagation.NESTED
- 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于Propagation.REQUIRED。
- 【外部事务回滚,内部事务一定会回滚;内部事务回滚,外部事务继续执行】
Propagation.REQUIRES_NEW、Propagation.NESTED、Propagation.REQUIRED 对比 定义serviceA.methodA()以Propagation.REQUIRED修饰; | |||
异常状态 | Propagation.REQUIRES_NEW (两个独立事务) | Propagation.NESTED (B的事务嵌套在A的事务中) | Propagation.REQUIRED (同一个事务) |
methodA抛异常 methodB正常 | A回滚,B正常提交 | A与B一起回滚 | A与B一起回滚 |
methodA正常 methodB抛异常 | 1.如果A中捕获B的异常,并没有继续向上抛异常,则B先回滚,A再正常提交; 2.如果A未捕获B的异常,默认则会将B的异常向上抛,则B先回滚,A再回滚 | B先回滚,A再正常提交 | A与B一起回滚 |
methodA抛异常 methodB抛异常 | B先回滚,A再回滚 | A与B一起回滚 | A与B一起回滚 |
methodA正常 methodB正常 | B先提交,A再提交 | A与B一起提交 | A与B一起提交 |
isolation:事务隔离级别
和数据库的事务隔离级别含义是相同的,只是多了default。区别是isolation指定的是当前连接会话的事务隔离级别,而数据库指的是当前数据库全局的事务隔离级别。当二者不相同时,以当前会话的隔离级别为准。
- Isolation.DEFAULT
- 使用数据库默认的事务隔离级别
- Isolation.READ_UNCOMMITTED
- 读未提交
- Isolation.READ_COMMITTED
- 读已提交
- Isolation.REPEATABLE_READ
- 可重复读
- Isolation.SERIALIZABLE
- 串行化
readOnly
- 设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false
- 如果设置为true却执行了写的操作会发生什么呢?后面写demo跑一下
timeout
- 设置事务的超时秒数,默认值为-1表示永不超时。超时事务将会回滚。
rollbackFor
- 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:
- 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
- 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName
- 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。
- 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")
- 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})
noRollbackFor
-
该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。
-
指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
-
指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName
- 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。
- 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException")
- 指定多个异常类名称:@Transactional(noRollbackForClassName={"RuntimeException","Exception"})
使用
- @Transactional要修饰在接口实现类或接口实现方法上,而不是接口类中
- 方法上注解属性会覆盖类注解上的相同属性
- 父类的声明的@Transactional会对子类的所有方法进行事务增强;子类覆盖重写父类方式可覆盖其@Transactional中的声明配置
- 只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰
- @Transactional修饰 public 的方法才起作用
- 接口中异常(运行时异常)被捕获而没有被抛出,将不会进行事务回滚
原理
【todo 我不配,用好了再学习一下】
参考
透彻的掌握 Spring 中 @transactional 的使用
Spring中的@Transactional(rollbackFor = Exception.class)属性详解
Spring事务隔离级别与Mysql InnoDB事务隔离级别的关系
关于Spring事务的理解(Controller可以使用@Transactional)
spring boot @Transactional注解事务不回滚不起作用无效
springBoot service 事务注解@Transactional不起作用的解决
SpringBoot @Transactional 中捕获异常并回滚解决方法
Springboot中声明事务@Transactional,为何有时候声明了事务报异常数据却不会回滚
Spring Boot 中使用 @Transactional 注解配置事务管理