Spring-事务传播级别
一个Service中的非事务方法调用事务方法
如果一个Service中有a和b两个method,其中a没有加上事务注解,而b加上了事务注解,a调用了b,具体如下:
@Service
class Service{
public void a(){
b();
}
@Transactional(propagation=PROPAGATION_REQUIRED)
public void b(){
//do something.
}
}
如果在别的地方注解了Service的bean,并调用了a方法,那么b方法是不走事务的。
因为Spring中注解事务是通过动态代理来实现的,在调用a方法时,由于a方法时没有事务注解的,因此在执行a方法时默认走的connection的auto_commit为true,也就是a方法没有走事务管理,默认设置的是auto_commit为true,这样之后的每个sql都会自动提交,而不会包含在整体的transaction中,因此在执行b()方法时,不会更新auto_commit为false,也就无法走事务管理了。
解决方法
- 将b()提取到新的service类中,注入进来
- 在当前方法获取类的代理对象,通过代理对象调用b
2.1 springboot启动类加上注解: @EnableAspectJAutoProxy(exposeProxy = true)获取使用上下文对象ApplicationContext 获取该对象
2.2 方法内部获取代理对象,再调用方法
try{
Service proxyService = (Service )AopContext.currentProxy();
proxyService.b();
} catch(Exception e){
// do exception
}
PROPAGATION_REQUIRED
默认为REQUIRED,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t2(){
do2();
}
在两个方法的事务传播级别都是PROPAGATION_REQURIED的时候。
如果调用t1,会开启一个事务,在t1内部调用t2,t2不会就不会再开启一个新的事务,t2会直接加入r1的事务中执行,这样如果在执行r2的时候出了异常导致事务回滚,则t2都会回滚。如果t1的pre()和t2都执行成功了,但是在执行post()抛出了异常导致事务回滚,则pre()、t2()和pre()()都会回滚。
如果不通过t1而单独调用t2,则会开启一个事务。
PROPAGATION_REQURIED所有方法公用一个事务,要么一起成功提交,要么一起失败回滚。
如果执行的方法要求一起执行成功或者回滚,则选择该事物传播级别。
PROPAGATION_REQUIRES_NEW
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_REQUIRED_NEW)
pubilc void t2(){
do2;
}
在事务的隔离级别是PROPAGATION_REQUIRED_NEW的时候。
如果调用t1,会开启一个事务,在方法内部调用t2,t2会自己再开启一个事务,然后t2在自己的事务内部执行,如果t2执行失败抛出异常,那么事务B会进行回滚,事务A不会受到影响可以继续执行,如果在t2执行成功,t2的事务会单独进行提交;B提交事务之后接着执行A的post()()方法,如果执行成功提交A的事务,如果抛出异常,则只回滚A的事务,对B的事务不会进行影响,也就说B的事务不会进行回滚。
如果不通过t1而单独调用t2,则会开启一个事务。
PROPAGATION_REQURIED_NEW所有方法使用各自的事务,各自提交或者回滚各自的事务,相互之间不会造成影响。
如果方法要求各自事务独立,不能进行相互影响,则选择本事务传播级别。
新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_SUPPORTS)
pubilc void t2(){
do2;
}
在事务的隔离级别是PROPAGATION_SUPPORTS的时候。
如果调用t1,会开启一个事务,在t1内部调用t2,由于t1已经存在开启尚未提交的事务,t2会直接加入t1的事务中执行,这样如果在执行t2的时候出了异常导致事务回滚,则B的方法和A的方法都会回滚。如果A的pre()()和t2都执行成功了,但是在执行post()()方法的时候抛出了异常导致事务回滚,则post()()、t2()和pre()()都会回滚。
如果不通过t1而单独调用t2,则t2不会开启事务,直接会以非事务的方式执行。
PROPAGATION_SUPPORTS如果存着事务就加入和PROPAGATION_REQUIRED传播级别一致,如果当前不存在事务,则不会创建新的事务,以非事务的方式执行。
如果方法要求一起执行成功或者回滚,单独执行时候以非事务方式执行,则选择该事物传播级别。
PROPAGATION_MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_MANDATORY)
pubilc void t2(){
do2;
}
在事务的隔离级别是PROPAGATION_MANDATORY的时候。
如果调用t1,会开启一个事务,在t1内部调用t2,由于t1已经存在开启尚未提交的事务,t2会直接加入t1的事务中执行,这样如果在执行t2的时候出了异常导致事务回滚,则B的方法和A的方法都会回滚。如果A的pre()()和t2都执行成功了,但是在执行post()()方法的时候抛出了异常导致事务回滚,则post()()、t2()和pre()()都会回滚。
如果不通过t1而单独调用t2,则t2会直接报错,因为t2的事务传播级别是PROPAGATION_MANDATORY,而其不允许在没有事务的环境下执行。
PROPAGATION_SUPPORTS如果存着事务就加入和PROPAGATION_REQUIRED传播级别一致,如果当前不存在事务,会直接进行报错,不允许以非事务的方式执行。
如果方法要求一起执行成功或者回滚,单独执行时候不允许以非事务方式执行,则选择该事物传播级别。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_NOT_SUPPORTED)
pubilc void t2(){
do2;
}
在事务的隔离级别是PROPAGATION_NOT_SUPPORTED的时候。
如果调用t1,会开启一个事务,在t1内部调用t2,由于t1已经存在开启尚未提交的事务,t2不允许在事务内部执行,这时候事务就会挂起,在非事务的状态中执行t2,不管t2是执行成功还是执行失败,都不会对当前事务造成影响。如果A的pre()()和t2都执行成功了,但是在执行post()()方法的时候抛出了异常导致事务回滚,则post()()和pre()()会回滚,而t2不会受到任何影响,因为它是在非事务中执行的。
如果不通过t1而单独调用t2,则t2不会开启事务,直接会以非事务的方式执行。
PROPAGATION_NOT_SUPPORTED如果存着事务就挂起当前事务,以非事务的方式运行自己,如果当前不存在事务,则不会创建新的事务,以非事务的方式执行。
如果方法要求内部嵌套方法不会对外部方法事务造成影响并且内部方法不需要事务,单独执行时候以非事务方式执行,则选择该事物传播级别。
PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_NEVER)
pubilc void t2(){
do2;
}
在事务的隔离级别是PROPAGATION_NERVR的时候。
如果调用t1,会开启一个事务,在t1内部调用t2,由于t1已经存在开启尚未提交的事务,t2的传播级别为PROPAGATION_NEVER,其不允许在事务内部执行,所以这时候就会直接报错。
如果不通过t1而单独调用t2,则t2会直接在没有事务的环境中执行。
PROPAGATION_NERVER如果存着事务就直接报错,如果当前不存在事务,会以非事务的方式执行。
如果方法要求内部方法不允许在事务中执行,单独执行时候必须以非事务方式执行,则选择该事物传播级别。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
@Transactional(propagation=PROPAGATION_REQUIRED)
pubilc void t1(){
pre();
t2();
post();
}
@Transactional(propagation=PROPAGATION_NESTED)
pubilc void t2(){
do2;
}
在事务的隔离级别是PROPAGATION_NESTED的时候。
如果调用t1,会开启一个事务,在t1内部调用t2,由于t1已经存在开启尚未提交的事务,t2的传播级别为PROPAGATION_NESTED,会加入这个事务当中,但是在执行到t2之前会创建一个事务的回滚点(savepoint),然后执行t2,如果t2执行失败了,事务会进行回滚,但是这时指挥回滚到回滚点,也就是之后回滚B的操作,外部方法的操作不会回滚;如果B执行成功了,接着执行A的post()()方法,如果执行出错,则会回滚整个事务,也就是post()()、t2()和pre()()的执行都会进行回滚。
如果直接调用t2,则会开启一个事务,和PROPAGATION_REQUIRED传播级别一致。
如果方法要求内部方法出错只回滚自己,外部方法执行失败回滚所有,单独执行时候自动开启一个执行,则选择该事物传播级别。
https://blog.csdn.net/qq_36094023/article/details/90544286
https://zhuanlan.zhihu.com/p/256263914