Spring事务的七种传播行为
- 1.事务的传播行为是什么?
- 2.具体传播行为
- 2.1 REQUIRED ,默认,存在事务则加入该事务,不存在则新建一个事务
- 2.2 REQUIRES_NEW,每次新开启事务,新老事务相互独立
- 2.3 NESTED, 存在事务则嵌套当中执行,不存在事务则新建一个事务
- 2.4 SUPPORTS,存在则支持当前事务,不存在事务则以非事务执行。
- 2.5 NOT_SUPPORTED, 以非事务的方式运行,存在事务则把当前事务挂起
- 2.6 MANDATORY,强制事务执行,不存在事务则抛出异常。
- 2.7 NEVER,以非事务来运行,存在事务则抛出异常。
- 3.事务失效的场景
- 3.1 非public修饰的方法
- 3.2 自身调用
- 3.3 被 final、static 关键字修饰的类或方法
- 3.4 没有被 Spring 管理
- 3.5 数据库不支持事务
- 3.6 异常被捕获
- 3.7 异常类型错误
- 3.8 多线程调用
1.事务的传播行为是什么?
事务的传播行为就是指多个声明了事务的方法相互调用的时候存在事务嵌套的问题,事务在方法中如何传播。
比如说:methodA()调用methodB(),两个方法都开启了事务,那么methodB()是开启了一个新事物,还是继续在methodA()这个事务中执行呢?就取决与事务的传播行为。
所以,Spring 为了解决这个问题,定义了 7 种事务传播行为。
传播行为 | 含义 |
---|---|
REQUIRED | 默认的Sring事务传播级别,如果当前事务存在,则加入该事务,如果不存在 就新建一个事务。 |
REQUIRES_NEW | 不管是否存在事务,都会新开启一个事务,新老事务相互独立;外部事务抛出异常回滚不会影响内部事务的正常提交。 |
NESTED | 如果当前存在事务,则嵌套在当前事务中执行,如果当前没有事务,则新建一个事务,类似与REQUIRE_NEW。 |
SUPPORTS | 表示支持当前事务,如果当前不存在事务,则以非事务的方式执行。 |
NOT_SUPPORTED | 表示以非事务的方式运行,如果当前存在事务,则把当前事务挂起。 |
MANDATORY | 表示强制事务执行,若当前不存在事务,则抛出异常。 |
NEVER | 表示以非事务的方式来运行,如果当前存在事务,则抛出异常。 |
2.具体传播行为
2.1 REQUIRED ,默认,存在事务则加入该事务,不存在则新建一个事务
当前存在事务则加入该事务,不存在事务则新建一个事务
示例代码:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
userMapper.updateById(new TbUser(2, "李开", 33, 1)); // 更新用户
methodB(); //调用methodB()
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
userMapper.updateById(new TbUser(3, "李福", 22, 1)); //更新用户
int a = 1/0; // 发生异常
System.out.println("=====执行 methodB 完成=====");
}
结论:用户数据不会发生变化。当调用methodA()上下文没有事务就会开启一个事务,当执行methodB(),因为已经存在一个事务,methodB()不会开启新事务,而是加入到methodA()事务中执行。当‘int a = 1/0;’执行报错就会回滚methodA事务,而methodB也在methodA事务中,所有都会回滚。
2.2 REQUIRES_NEW,每次新开启事务,新老事务相互独立
不管是否存在事务,都会新开启一个事务,新老事务相互独立;外部事务抛出异常回滚不会影响内部事务的正常提交。
2.3 NESTED, 存在事务则嵌套当中执行,不存在事务则新建一个事务
如果一个活动的事务存在,则运行在一个嵌套事务中,如果没有活动事务,则按照PROPAGATION_REQUIRED属性执行。
2.4 SUPPORTS,存在则支持当前事务,不存在事务则以非事务执行。
如果存在事务则支持当前事务,如果不存在事务则无事务运行
2.5 NOT_SUPPORTED, 以非事务的方式运行,存在事务则把当前事务挂起
表示以非事务的方式运行,如果当前存在事务,则把当前事务挂起。
2.6 MANDATORY,强制事务执行,不存在事务则抛出异常。
如果存在一个事务,则事务的运行,没有事务则抛出异常。
2.7 NEVER,以非事务来运行,存在事务则抛出异常。
总是非事务的运行,如果存在一个活动的事务,则抛出异常。
3.事务失效的场景
在方法上添加@Transactional 注解后,Spring 会基于这个类生成一个代理对象,会将这个代理对象作为 bean,当使用这个代理对象的方法时,那么代理逻辑会先把事务的自动提交设置为 false,然后再去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会将事务进行提交,如果执行逻辑出现了异常,那么则会将事务进行回滚。默认情况下会对 RuntimeException 和 Error 进行回滚。可以利用 @Transactional 注解中的 rollbackFor 属性进行配置异常信息。
3.1 非public修饰的方法
@Transactional 只能用于 public 的方法上,否则事务不会生效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
3.2 自身调用
事务的管理是通过代理执行的方式生效的,如果是方法内部调用,没有经过 Spring 的代理类,就调用不到了
3.3 被 final、static 关键字修饰的类或方法
3.4 没有被 Spring 管理
没有注入到IOC容器的的类。
3.5 数据库不支持事务
比如 Mysql 的 Myisam 存储引擎是不支持事务的,只有 innodb存储引擎才支持。
3.6 异常被捕获
当异常被捕获后,而且没有再抛出,那么事务是不会回滚的。
3.7 异常类型错误
@Service
public class OrderServiceImpl implements OrderService
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
throw new Exception("更新错误");
}
}
}
这样事务也是不生效的,因为默认回滚的是:RuntimeException 和 Error,如果想触发其他异常的回滚,需要在注解上配置一下,如通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。
3.8 多线程调用
Spring 的事务是通过数据库连接来实现,而数据库连接是放在 ThreadLocal 里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的。