Spring
事务的7种传播行为
1.REQUIRED(默认): 如果当前没有事务,就创建事务;如果当前存在事务,就加入该事务.
2.REQUIRES_NEW: 新建事务,如果当前存在事务,则把当前事务挂起,这个方法会独立提交事务,不受外部事务的影响,同时自身异常也不会影响外部事务.
3.SUPPORTS: 如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式运行.
4.NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起.
5.MANDATORY: 如果当前存在事务,就加入该事务;当前没有事务,则抛出异常,即外部方法必须有事务(学个单词:mandatory-强制性的;强制的;法定的;义务的).
6.NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常,即外部方法必须无事务.
7.NESTED: 如果当前存在事务,则它成为外部事务的一个内嵌事务,只有等外部事务提交才能提交;如果当前没有事务,则该取值等价于REQUIRED。
记一种try catch
之后还发生回滚的情况
@Override
@Transactional(rollbackFor = Exception.class)
public void transactionA() {
try {
T t = new T();
t.setK(11);
this.baseMapper.insert(t);
TransactionBService.transactionB();//TransactionBService通过Spring容器注入
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void transactionB() {
T t = new T();
t.setK(22);
this.baseMapper.insert(t);
throw new RuntimeException("error");
}
A方法和B方法,都标记了@Transactional(rollbackFor = Exception.class)
,在A方法中调用了B方法,B方法报错,A方法和B方法都进行了回滚,没有插入数据库.
原因分析: 首先我们要知道,外部经过Spring
容器调用service
的方法,事务才会生效,service
类内部互相调用方法事务是不会生效的,Spring
事务的实现基础是AOP
, 而AOP
的原理是动态代理,若是类自身的调用,而不是代理对象去调用,就不会产生AOP
,这样Spring
就不能把你的代码织入到约定的流程中去.
接着讲上面这个问题,A方法调用B方法,会在B方法的时候启动代理,然后再去执行B的方法.代理发现当前存在事务,且B方法的传播行为为Propagation.REQUIRED
,方法的传播行为为Propagation.REQUIRED
,就会把B的事务并入到当前事务中.当B方法抛出异常,就已经把A方法的事务(当前事务)标记为回滚,然后抛出异常;这个时候A方法try catch
捕获异常,但事务已经是回滚状态了.
@Transactional(rollbackFor = Exception.class)
Exception分为运行时异常RuntimeException和非运行时异常.当@Transactional
注解作用于类上时,该类的所有public方法都将具有将传播类型的事务,我们也可以把该注解在方法上,这样不同方法就可以指定各自的事务传播类型.
在 @Transactional
注解中如果不配置rollbackFor
属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class
,可以让事务在遇到非运行时异常时也回滚.
额外基础小知识:
运行时异常
是指RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理.这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生.
非运行时异常
是RuntimeException以外的异常,类型上都属于Exception类及其子类.如IOException、SQLException等以及用户自定义的Exception异常.对于这种异常,JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过.