事务
# spring事务传播
为什么要有事务传播呢?默认情况下,spring是使用aop来实现事务的,我们只需要在方法上面加上 @Transactional
注解就可以了,但是这个时候会有几个问题比如 serviceA 方法调用了 serviceB 方法,但两个方法都有事务,这个时候如果 serviceB 方法异常,是让 serviceB 方法提交,还是两个一起回滚?
这里我们就要用到事务传播了,spring提供了7中事务传播行为(使用@Transaction(Propagation=XXX)
设置决定)
# PROPAGATION_REQUIRED (默认)
- 支持当前事务,如果当前没有事务,则新建事务
- 如果当前存在事务,则加入当前事务,合并成一个事务
# REQUIRES_NEW
- 新建事务,如果当前存在事务,则把当前事务挂起
- 这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交
# NESTED
- 如果当前存在事务,它将会成为父级事务的一个子事务,方法结束后并没有提交,只有等父事务结束才提交
- 如果当前没有事务,则新建事务
- 如果它异常,父级可以捕获它的异常而不进行回滚,正常提交
- 但如果父级异常,它必然回滚,这就是和
REQUIRES_NEW
的区别
# SUPPORTS
- 如果当前存在事务,则加入事务
- 如果当前不存在事务,则以非事务方式运行,这个和不写没区别
# NOT_SUPPORTED
- 以非事务方式运行
- 如果当前存在事务,则把当前事务挂起
# MANDATORY
- 如果当前存在事务,则运行在当前事务中
- 如果当前无事务,则抛出异常,也即父级方法必须有事务
# NEVER
- 以非事务方式运行,如果当前存在事务,则抛出异常,即父级方法必须无事务
一般用得比较多的是 PROPAGATION_REQUIRED
, REQUIRES_NEW
;
REQUIRES_NEW
一般用在子方法需要单独事务
还有spring本身不提供事务功能,事务功能是数据库提供的
参考
- Spring事务传播行为详解 - SegmentFault 思否 (opens new window)
- spring 事务的传播机制看这篇就够了 - SegmentFault 思否 (opens new window)
# 事务隔离的五种级别
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
# 事务失效的原因
主要是这八点:
- 数据库引擎是否支持事务(比如MyISAM就不支持事务)
- 注解所在的类是否被加载成Bean
- 注解所在的方法是否为public所修饰
- 是否发生了自调用机制——同一个类之中的方法相互调用
- 所用的数据源是否加载了事务管理器
@Transactional
的扩展配置propagation是否正确(七种传播行为)- 异常被吃掉了——自己try自己catch
- 异常抛出错误——抛的不是RuntimeException
编辑 (opens new window)
上次更新: 2021/04/23, 21:43:16