一、事务
事务的传播机制
事务的传播机制是指 在两个或多个事务内,事务相互之间的影响或传递
事务传播机制的类别
- REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务,这是最常见的选择
- SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行
- MANDATORY: 支持当前事务,如果当前没有事务,就抛出异常
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
- NOT_SUPPORTED:以非事务方式操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
REQUIRED
这个是事务的默认取值,当 ServiceA.methodA 的事务级别是 REQUIRED
, ServiceB.methodB 也是 REQUIRED
时,ServiceA.methodA 起了一个事务
ServiceB.methodB 被 ServiceB.methodB 调用,ServiceB.methodB 发现自己在事务中,就不在新起事务了,如果 ServiceA.methodA 没有起事务
则 ServiceB.methodB 自己起事务。
当前存在事务中则加入当前事务,如果不在事务中则起一个事务
代码演示 场景1:在上层方法加入事务注解,下方不加
@Autowired
private TransactionalService transactionalService;
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
发生异常,失败回滚,结论: 当前存在事务中则加入当前事务
代码演示 场景2:上层事务关闭,开启下层事务
@Autowired
private TransactionalService transactionalService;
// @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
发生异常,失败回滚,但是 上层方法保存成功,结论: 如果不在事务中则起一个事务
SUPPORTS
当标注的方法在事务中,则按事务的方式执行,此时类似标注 REQUIRED
, 外层回滚一起回滚,其本身不会发生事务,当外层没有事务时,则按没有事务的方式执行
如果调用此方法的方法存在事务,则本方法存在事务,与调用者一体事务,如果调用者没有事务,此方法以非事务方式运行
代码演示 场景1:上层事务开启 REQUIRED
下层事务开启 SUPPORTS
@Autowired
private TransactionalService transactionalService;
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
发生异常,失败回滚,未插入数据,结论:如果调用此方法的方法存在事务,则本方法存在事务,与调用者一体事务
代码演示,场景2 : 上层事务关闭,下层事务开启 SUPPORTS
@Autowired
private TransactionalService transactionalService;
// @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果
执行结果,上层事务保存成功,发生异常后 下层事务没有回滚,结论 :如果调用者没有事务,此方法以非事务方式运行
MANDATORY
调用者必须存在事务,如果调用者没有存在事务,则抛出异常
代码演示, 场景1:上层事务关闭,下层开启 MANDATORY
事务
@Autowired
private TransactionalService transactionalService;
// @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
上层事务保存成功,下层因为没有包含事务,抛出异常,中止执行
结论:调用者必须存在事务,如果调用者没有存在事务,则抛出异常
REQUIRES_NEW
当 ServiceA.methodA 的事务级别是 REQUIRED
, ServiceB.methodB 是 REQUIRES_NEW
时, ServiceA.methodA 起了一个事物, ServiceB.methodB 被
ServiceA.methodA 调用,则挂起 ServiceA.methodA 的事务,自己重新起事务,这两个事务是隔离的,当 ServiceB.methodB 提交之后,才继续 ServiceA.methodA 的事务,他与 REQUIRED
的区别在于回滚程度, 因为 ServiceB.methodB 是新起一个事务,那么就是存在两个不同的事务,ServiceA.methodA 的事务失败回滚,也不会影响到 ServiceB.methodB 的事务。 如果 ServiceB.methodB 发生了异常回滚,ServiceA.methodA 可以选择是否捕捉异常继续提交,或者 与其一起回滚。
重新起事务,不管调用者是否存在事务,如果存在则挂起调用者事务,等待本身事务执行完毕,再执行调用者事务。事务相互隔离,回滚失败与否 互不影响,但是抛出的异常外层事务是可以捕获的
代码演示,场景1:上层事务开启 REQUIRED
, 下层事务开启 REQUIRES_NEW
, 异常模拟在 下层事务中
@Autowired
private TransactionalService transactionalService;
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
失败回滚,上层方法也没有插入,这样看, REQUIRES_NEW
和上层事务好像是一体的,看一下场景2
代码演示 场景2: 上层事务开启 REQUIRED
下层事务开启 REQUIRES_NEW
模拟异常发生在,上层事务
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
// 模拟异常
int i = 1 / 0;
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
// int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
下层事务保存成功,上层事务发生回滚,结论:标注 REQUIRES_NEW
的事务,是单独的,与上层事务无关。
代码演示,场景3: 上层事务开启 REQUIRED
下层事务开启 REQUIRES_NEW
模拟异常发生在下层事务,上层事务捕捉异常
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
try {
transactionalService.saveChild();
} catch (Exception e) {
log.info("出错了");
}
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
外层事务可捕捉内层事务异常
捕捉:内层事务回滚,外层事务照常提交
不捕捉:内外层事务一起回滚。
NOT_SUPPORTED
当标注的方法在事务中,则按照没有事务的方式执行,此时会挂起调用者事务,等待本身执行完毕后,再执行调用者事务,与 REQUIRES_NEW
类似,事务相互隔离,但是标注 NOT_SUPPORTED
是以没有事务的方式执行的,操作失败也不会回滚,但是异常能被外层捕获。
隔离外层事务,且本身不起事务,当外层有事务时挂起外层事务,等待本身执行完毕,再执行外层事务。外层回滚不影响本身操作,但抛出的异常外层是可以捕获到的
代码演示 场景1:上层事务关闭,下层事务为 NOT_SUPPORTED
模拟异常发生在 下层事务
// @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
上层保存成功,下层异常之后未保存,结论:NOT_SUPPORTED
是以没有事务的方式执行的,操作失败也不会回滚,但是异常能被外层捕获。
代码演示 场景2:上层事务开启 REQUIRED
,下层事务开启 NOT_SUPPORTED
模拟异常发生在 下层
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
上层事务被回滚,下层异常之后不执行,结论:NOT_SUPPORTED
是以没有事务的方式执行的,操作失败也不会回滚,但是异常能被外层捕获。
NEVER
当 ServiceA.methodA 的事务级别是 REQUIRED
, ServiceB.methodB 是 NEVER
时, ServiceA.methodA 起了一个事务, ServiceB.methodB 被 ServiceA.methodA 调用时,ServiceB.methodB 就会抛出异常
不能在事务中运行,否则,自身抛出异常
代码演示,场景1:上层事务开启 REQUIRED
下层事务开启 NEVER
模拟异常发生在下层
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:无保存记录,下层事务抛出异常
结论:NEVER
不能在事务中运行,否则,自身抛出异常
NESTED
这个级别和 REQUIRES_NEW
理解基本一样,不同在于 NESTED
不是重新起一个事务,而是建立一个 savePoint
, 提交时间和外层事务一起提交,一起回滚
但是好处在于 他存在一个 savePoint
例如自身 失败回滚,外层可以选择新分支执行并尝试完成自己的事务,外层没有事务时,独立运行事务
开始一个嵌套事务,他是属于调用者的子事务,嵌套事务开始时,他会取得一个 savePoint 如果这个嵌套事务失败,我们将回滚到此 savePoint, 外层可以继续执行。嵌套事务是外部事务的一部分,只有外部事务结束后他才会被提交。外部事务失败回滚,内部也会回滚
代码演示, 场景1 : 上层开启事务 REQUIRED
下层开启事务 NESTED
模拟异常发生在 下层事务
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
结果:
未插入任何数据,因为 上层没有捕捉下层事务 的异常情况
代码演示,场景2:上层开启事务 REQUIRED
下层开启事务 NESTED
模拟异常发生在 下层事务,上层捕捉异常
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
try {
transactionalService.saveChild();
} catch (Exception e) {
log.info("出错了");
}
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
// 模拟异常
int i = 1 / 0;
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
saveParent 保存成功,saveChild 发送异常回滚,结论 NESTED
自身 失败回滚,外层可以选择新分支执行并尝试完成自己的事务
仅仅是这样是不是觉得
NESTED
和REQUIRES_NEW
一样,外层可以决定 内部异常 外部是否回滚。来,往下看,上层发生异常下层也要回滚
代码演示,场景3:上层开启事务 REQUIRED
下层开启事务 NESTED
模拟异常发生在 上层事务
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void testTrans(){
transactionalService.saveParent();
transactionalService.saveChild();
// 模拟异常
int i = 1 / 0;
}
ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB
@Override
public void saveParent() {
this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
}
@Override
@Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
public void saveChild() {
saveChild1();
saveChild2();
}
private void saveChild1(){
this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
}
private void saveChild2(){
this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
}
执行结果:
上层执行失败,下层也要回滚,与 REQUIRES_NEW
不同,REQUIRES_NEW
是另外再开启一个事务,而 NESTED
是嵌套在上层事务中的子事务,当外层没有事务时,独立运行事务。
事务失效的一些场景
@Transactional
加在 非 public 方法上 , 事务通过 aop 切面 进行拦截,非 public 方法,会直接返回 null- 在本对象内调用,如 this.method ,通过 this 调用,没有代理对象,除非,在本类中,注入自己本身
- 事务方法被
final
或static
修饰 , 这样修饰,代理类无法重写- 数据库不支持事务
- 未开启事务
- 事务对象未被 Spring 管理
- 进行 try catch 捕捉, 仅是不回滚事务,可以在
@Transactional
指定要回滚的异常类型
注意事项:
在测试的过程中遇到的一些问题
1.一定要使用两个 Service 来测试,不要在 单元测试上直接加 事务,因为 Spring 的事务 是根据
代理
进行拦截的,不通过注入无法创建代理对象