继上一篇文章,我们讲到了事务的传播行为,具体是那七个。我们在罗列一遍:
PROPAGATION_REQUIRED
如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
PROPAGATION_MANDATORY
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
PROPAGATION_REQUIRES_NEW
总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
PROPAGATION_NOT_SUPPORTED
总是非事务地执行,并挂起任何存在的事务
PROPAGATION_NEVER
总是非事务地执行,如果存在一个活动事务,则抛出异常
PROPAGATION_NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
我们就转账例子说一下。 比如,在转账的services中有个两个方法,方法A和方法B。
方法A
ServiceA {
read A =100 ;
If(doSome()){
A = A + 1;
}else{
A = A - 1;
}
Update A;
Commit;
}
1
2
3
4
5
6
7
8
9
10
方法B
ServiceB {
void doOther() {
read B ;
B = B – 1;
Update B;
If(B>0){
Commit;
Return ture;
}else{
Rollback;
Return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
(1)REQUIRED——必须有事务
指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是Spring默认的事务传播行为。
比如说,Servicedo的doOther()方法上加上了传播行为PROPAGATION_REQUIRED。若doSome()方法在调用doOther()方法时就是在事务内运行的,则doOther()方法的执行也加入到该事务内执行。若doSome()方法在调用doOther()方法时没有在事务内执行,则doOther()方法会促使doSome()创建一个事务,并在加入其中执行。在doSome()或者在doOther()内的任何地方出现异常,事务都会被回滚。即使doOther()的事务已经被提交,doSome()在接下来fail要回滚,doOther()也要回滚。
简单来说,谁调加了REQUIRED的方法,谁就要具有一个事务,被调用的方法可有可无事务,但是调他的必须有事务
(2)SUPPORTS ——支持事务
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。 定义在再方法doOther()上,定义在doSome()上。
(3)MANDATORY ——托管事务
指定的方法必须在当前事务内执行。就是说,他只能被一个父事务调用。若当前没有事务,则直接抛出异常。 定义在再方法doOther()上,定义在doSome()上。doSome()是father。
(4)REQUIRES_NEW ——需要新事务
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
比如,我们设计ServiceA.doSome()的事务级别为PROPAGATION_REQUIRED,ServiceB.doOther()的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.doOther()的时候,ServiceA.doSome()所在的事务就会挂起,ServiceB.doOther()会起一个新的事务,等待ServiceB.doOther()的事务完成以后,他才继续执行。
优先级别:PROPAGATION_REQUIRES_NEW >PROPAGATION_REQUIRES
PROPAGATION_REQUIRED与PROPAGATION_REQUIRED 的事务区别:在于事务的回滚程度。因为ServiceB.doOther()是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.doOther()已经提交,那么ServiceA.doSome()失败回滚,ServiceB.doOther()是不会回滚的。如果ServiceB.doOther()失败回滚,如果他抛出的异常被ServiceA.doSome()捕获,ServiceA.doSome()事务仍然可能提交。
(5)NOT_SUPPORTED ——不支持事务
指定的方法不能在事务环境中执行,若当前存在事务,就将当前事务挂起。
比如ServiceA.doSome()的事务级别是PROPAGATION_REQUIRED ,而ServiceB.doOther()的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.doOther()时,ServiceA.doSome()的事务挂起,而他以非事务的状态运行完,再继续ServiceA.doSome()的事务。一般,在非常耗时查询的时候就可以使用Not Support,挂起这个查询方法,让其他方法先行,避免事务时间超长。
优先级别:PROPAGATION_NOT_SUPPORTED>PROPAGATION_REQUIRED
(6)NEVER ——禁用事务
指定的方法不能在事务环境下执行,若当前存在事务,就直接抛出异常。
假设ServiceA.doSome()的事务级别是PROPAGATION_REQUIRED,而ServiceB.doOther()的事务级别是PROPAGATION_NEVER ,那么ServiceB.doOther()就要抛出异常了。
简而言之:事务掉了拥有NEVER属性的方法统统要出错。
优先级别:PROPAGATION_NEVER>PROPAGATION_REQUIRED
(7)NESTED ——嵌套事务
指定的方法必须在事务内执行。若当前存在事务,则在嵌套事务内执行;若当前没有事务,则创建一个新事务。并且使用savepoint设置事务的回滚点。
那怎么使用savapoint()的,看下面这个例子
savapoint()使用
ServiceA {
void doSome() {
try {
//savepoint
ServiceB.doOther(); //PROPAGATION_NESTED 级别
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.doElse()等等
}
}
}
1
2
3
4
5
6
7
8
9
10
加入,ServiceB.doOther()失败回滚,那么ServiceA.doSome() 也会回滚到savepoint点上,ServiceA.doSome() 可以选择另外一个分支,比如ServiceC.doElse(),继续执行,来尝试完成自己的事务。
PROPAGATION_Nested与PROPAGATION_REQUIRES_NEW的区别是:PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
---------------------
作者:童小绿
from:https://blog.csdn.net/tsj11514oo/article/details/52389124?utm_source=copy