Spring 通过切面配置事务传播机制 PROPAGATION
默认 REQUIRED 支持当前事务,当前没有事务则新建
REQUIRED_NEW 挂起当前事务,新建事务连接
SUPPORTS 支持当前事务,如果没有,则以非事务方式执行(自动提交)
MANDATORY 支持当前事务,如果没有,则抛出异常
NOT_SUPPORTS 以非事务方式执行操作,如果当前事务存在,就把当前事务挂起
NEVER 以非事务方式执行,如果存在事务,则抛出异常
NESTED 实现事务分部分回滚
默认发生的异常都是符合回滚条件的异常
methodA开启事务@Transactional(propagation = Propagation.REQUIRED),写库前或者写库后发生异常,methodA都会发生回滚!
methodA方法嵌套methodB方法,其中methodA开启事务@Transactional(propagation = Propagation.REQUIRED),methodB方法没有添加事务注解:
● 情况1:(默认methodA中无异常发生)methodB方法抛出异常
methodA,methodB 会发生回滚
● 情况2:(默认methodA中无异常发生)methodB方法抛出异常,但是在methodA中将methodB抛出的异常try cache掉了,没有继续向外抛出
1、methodA不会发生回滚,
2、理论上methodB 也不会发生回滚,但是异常发生的位置会影响methodB写库操作是否能成功
2.1、如果异常是在methodB写库后发生,则写库是成功的,且不会发生回滚
2.2、如果异常是在methodB写库前发生,此时写库操作还未执行,此时数据未入库并不是因为发生了回滚,而是写库未执行造成的
● 情况3:在methodB执行前,methodA抛出异常
1、methodA发生回滚
2、理论上methodB也会发生回滚,此时methodB未执行
● 情况4:在methodB执行后,methodA抛出异常
1、methodA发生回滚
2、methodB发生回滚
此种情景methodA,methodB属于一个事务连接
methodA方法嵌套methodB方法,其中methodA开启事务@Transactional(propagation = Propagation.REQUIRED),methodB方法添加事务注解@Transactional(propagation = Propagation.REQUIRES_NEW):
● 情况1:(默认methodA中无异常发生) methodB方法抛出异常
1、methodA发生回滚,因为methodB发生异常导致
2、methodB发生回滚
● 情况2:(默认methodA中无异常发生) methodB方法抛出异常,但是在methodA中将methodB抛出的异常try cache掉了,没有继续向外抛出
1、methodA写库成功
2、methodB发生回滚
● 情况3:在methodB执行前,methodA抛出异常
1、methodA发生回滚
2、理论上methodB不会发生回滚,此时methodB未执行
● 情况4:在methodB执行后,methodA抛出异常
1、methodA发生回滚
2、理论上methodB不会发生回滚
此种情景methodA,methodB属于不同的事务连接,执行methodB的事务时,会将methodA的事务挂起。
methodA方法嵌套methodB方法,其中methodA开启事务@Transactional(propagation = Propagation.REQUIRED),methodB方法添加事务注解@Transactional(propagation = Propagation.NESTED)
,methodB方法添加事务注解@Transactional(propagation = Propagation.NESTED)
classA中
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){
try{
classB.methodB();
}catch(Exception e){
classB.methodC();
}
}
classB中
@Transactional(propagation = Propagation.NESTED)
public void methodB(){
}
classB中
@Transactional(propagation = Propagation.NESTED)
public void methodC(){
}
methodA在执行methodB前会保存一个savePoint,
当methodB发生异常时,会回滚到savePoint,然后再去执行后续的代码 完善了情景1-情况2中的问题
此种情景methodA,methodB,methodC属于同一个事务连接,该事务具备部分回滚的能力
methodA方法嵌套methodB方法,其中methodA不开启事务,methodB方法添加事务注解@Transactional(propagation = Propagation.REQUIRED):
1、methodA不回滚
2、methodB会回滚
1、methodA不回滚
2、methodB不回滚
问题:
1、情景1-情况2中,如果methodB也添加@Transactional(propagation = Propagation.REQUIRED),则methodB,methodA都发生回滚
注:情景1-情景4 methodA,methodB在不同的类中
public void methodA() {
OssTask taskCc = new OssTask().setName("testa");
taskCc.setProjectId("2");
taskService.save(taskCc);
methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
OssTask taskCc = new OssTask().setName("testb");
taskCc.setProjectId("3");
taskService.save(taskCc);
int w = 2/0; // 抛出异常
}
methodA,methodB 都未发生回滚
public void methodA() {
OssTask taskCc = new OssTask().setName("testa");
taskCc.setProjectId("2");
taskService.save(taskCc);
ModuleServiceImpl moduleService = (ModuleServiceImpl) AopContext.currentProxy();
moduleService.methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
OssTask taskCc = new OssTask().setName("testb");
taskCc.setProjectId("3");
taskService.save(taskCc);
int w = 2/0;
}
此时methodB中事务发生回滚,methodA中无事务所以不发生回滚
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
OssTask taskCc = new OssTask().setName("testa");
taskCc.setProjectId("2");
taskService.save(taskCc);
methodB();
}
// @Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
OssTask taskCc = new OssTask().setName("testb");
taskCc.setProjectId("3");
taskService.save(taskCc);
int w = 2/0;
}
methodA,methodB 发生回滚
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
OssTask taskCc = new OssTask().setName("testa");
taskCc.setProjectId("2");
taskService.save(taskCc);
methodB();
int w = 2/0;
}
// @Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
OssTask taskCc = new OssTask().setName("testb");
taskCc.setProjectId("3");
taskService.save(taskCc);
}
同上,发生回滚
综合情景5和情景6:
Spring的声明式事务是基于Aop实现的,将业务逻辑和事务逻辑进行解耦,在Aop的代理下,只有目标方法通过外部调用,目标方法才有代理对象来进行管理,反之如果是内部调用,目标方法还是由原对象管理,此时改方法并没有得到增强处理,也就是会造成自调用问题(事务失效)。
重点在于,Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。
而在同一个class中,方法A调用方法B,调用的是原对象的方法,而不通过代理对象。所以Spring无法切到这次调用,也就无法通过注解保证事务性了。
也就是说,在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用。