Spring声明式事务中的传播特性(详细)
文章目录
1.事务的传播特性:
事务的传播特性指的是:当一个事务方法被另一个事务方法调用时,这个事务应该如何进行控制。
7种传播特性:
2、REQUIRED属性
- 使用: @Transactional(propagation = Propagation.REQUIRED)
- 描述:如果有事务在运行,当前方法就在这个事务内运行,否则,就启动一个新的事务,并在它的事务中运行。
1、方法a和方法b都不进行事务添加,只是普通方法,方法c是事务方法
方法c调用方法a和方法b
//事务方法c
c(){
a();//普通方法
b();//普通方法
}
结果:方法b、事务方法c、只要其中一个出现一异常,事务将会回滚。
2、方法a和方法b都设置为:REQUIRED,方法c是事务方法
先用表格展示,后面进行演示:
方法a和方法b都设置为:REQUIRED,方法c是事务方法,方法c调用方法a和方法b
@Transactional(propagation = Propagation.REQUIRED)
a(){};
@Transactional(propagation = Propagation.REQUIRED)
b(){};
//事务方法c
@Transactiona
c(){
a();
b();
}
表格:
方法a | 方法b | 方法c | 结果 |
---|---|---|---|
正常 | 正常 | 正常 | 正常 |
异常 | 正常 | 正常 | 全部回滚 |
正常 | 异常 | 正常 | 全部回滚 |
异常 | 异常 | 正常 | 全部回滚 |
正常 | 正常 | 异常 | 全部回滚 |
总结:可见三个方法在同一个事务中运行,若其中一个异常,则会回滚。
演示:
-
事务方法c调用方法a(正常)和方法b(正常)
//事务方法c c(){ a();//正常 b();//正常 }
结果:正常运行
-
事务方法c调用方法a(异常)和方法b(正常)
//事务方法c c(){ a();//异常 b();//正常 }
结果:全部方法回滚;
-
事务方法c调用方法a(正常)和方法b(异常)
//事务方法c c(){ a();//正常 b();//异常 }
结果:全部回滚
-
事务方法c调用方法a(异常)和方法b(异常)
//事务方法c c(){ a();//异常 b();//异常 }
结果:全部回滚
-
事务方法c(异常)调用方法a(正常)和方法b(正常)
//事务方法c c(){ int i=1/0;//异常,无论异常在哪个位置 a();//正常 b();//异常 }
结果:无论事务c中的异常在哪个地,都全部回滚。
总结:方法a和方法b都设置为:REQUIRED,方法c是事务方法。调用方法a和方法b,两个方法都会在c方法的事务中运行,三者只要一个一异常,则全部回滚。
3、方法a和方法b都设置为:REQUIRED,方法c是普通方法
此时,方法a和方法b都将各自新启动事务并各自执行
先用表格展示,后面进行演示:
方法a和方法b都设置为:REQUIRED,方法c不是事务方法,方法c调用方法a和方法b
@Transactional(propagation = Propagation.REQUIRED)
a(){};
@Transactional(propagation = Propagation.REQUIRED)
b(){};
//普通方法c
c(){
a();
b();
}
方法a | 方法b | 方法c | 结果 |
---|---|---|---|
正常 | 正常 | 正常 | 全部正常 |
异常 | 正常 | 正常 | ac回滚,b不回滚 |
正常 | 异常 | 正常 | ac回滚,b不回滚 |
异常 | 异常 | 正常 | 全部回滚 |
正常 | 正常 | 异常 | c回滚,ab不回滚 |
总结:c是普通方法,则a和b会各自新启动事务,各自事务互不影响
-
普通方法c调用方法a(正常)和方法b(正常)
//普通方法c c(){ a();//正常 b();//正常 }
结果:正常运行
-
普通方法c调用方法a(异常)和方法b(正常)
//普通方法c c(){ a();//异常 b();//正常 }
结果:a回滚,c回滚,b不回滚。
-
普通方法c调用方法a(正常)和方法b(异常)
//普通方法c c(){ a();//正常 b();//异常 }
结果:b回滚,c回滚,a不回滚
-
普通方法c调用方法a(异常)和方法b(异常)
//普通方法c c(){ a();//异常 b();//异常 }
结果:a、b、c都回滚
-
普通方法c(异常)调用方法a(正常)和方法b(正常)
//普通方法c c(){ int i=1/0;//无论异常在哪个位置 a();//正常 b();//正常 }
结果:c回滚,a不回滚,b不回滚
总结:方法a和方法b都设置为:REQUIRED,方法c是普通方法。方法c调用方法a和方法b,由于方法c不是事务方法,方法a和方法b都回各自新建事务。两者互不影响
4、方法a和方法b都设置为:REQUIRED,方法c是事务方法,进行异常捕获
-
a(或b)有异常,在c中进行捕获。全部回滚。
-
a(或b)有异常,在a(或b)中捕获,a(或b)回滚,其他不回滚
-
a,b正常,c中有异常并捕获,全部回滚
3.REQUIRED_NEW属性
- 使用: @Transactional(propagation = Propagation.REQUIRED_new)
- 描述:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务在运行,则将他挂起。
- 理解:如果调用该方法是事务方法,那么该方法的事务将挂起,启动新事务,在自己的事务内运行,不受挂起事务的影响,但是挂起事务会收到新启动事务的影响
1、方法a和方法b都设置为:REQUIRED_NEW,方法c是事务方法
先用表格展示,后面进行演示:
方法a和方法b都设置为:REQUIRED_NEW,方法c是事务方法,方法c调用方法a和方法b
@Transactional(propagation = Propagation.REQUIRED_NEW)
a(){};
@Transactional(propagation = Propagation.REQUIRED_NEW)
b(){};
//事务方法c
@Transactiona
c(){
a();
b();
//int i=1/0;
}
方法a | 方法b | 方法c | 结果 |
---|---|---|---|
正常 | 正常 | 正常 | 正常 |
异常 | 正常 | 正常 | a,c回滚,b不回滚 |
正常 | 异常 | 正常 | b,c回滚,a不回滚 |
异常 | 异常 | 正常 | 全部回滚 |
正常 | 正常 | 异常 | c回滚,ab不回滚 |
总结:方法a和方法b都会启动新事物,不会相互影响,也不会受c事务的影响,但是c事务会受到a和b事务结果的影响。
演示:
-
事务方法c调用方法a(正常)和方法b(正常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED_NEW) b(){}; //事务方法c @Transactiona c(){ a();//正常 b();//正常 }
结果:正常运行
-
事务方法c调用方法a(异常)和方法b(正常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED_NEW) b(){}; //事务方法c @Transactiona c(){ a();//异常 b();//正常 }
结果:a回滚,c回滚,b不回滚
-
事务方法c调用方法a(正常)和方法b(异常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED_NEW) b(){}; //事务方法c @Transactiona c(){ a();//正常 b();//异常 }
结果:b回滚,c回滚,a不回滚
-
事务方法c调用方法a(异常)和方法b(异常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED_NEW) b(){}; //事务方法c @Transactiona c(){ a();//异常 b();//异常 }
结果:全部回滚
-
事务方法c(异常)调用方法a(正常)和方法b(正常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED_NEW) b(){}; //事务方法c @Transactiona c(){ int i=1/0; a();//正常 b();//正常 }
结果:c回滚,a,b不回滚
总结:从上面的案例是可以发现,只要方法a和方法b其中一个异常,事务c都会回滚,但是c异常,a和b都不会回滚。可见新启动的事务(a,b)不受挂起事(c)务的影响,但是新启动的事务(a,b)会影响挂起的事务(c).
2、方法a设置为REQUIRED_NEW,方法b都设置为:REQUIRED,方法c是事务方法
先用表格展示,后面进行演示:
法a设置为REQUIRED_NEW,方法b都设置为:REQUIRED_,方法c是事务方法。方法c调用方法a和方法b
@Transactional(propagation = Propagation.REQUIRED_NEW)
a(){};
@Transactional(propagation = Propagation.REQUIRED)
b(){};
//事务方法c
@Transactiona
c(){
a();
b();
//int i=1/0;
}
方法a | 方法b | 方法c | 方法a和b的先后顺序 | 结果 |
---|---|---|---|---|
正常 | 正常 | 正常 | a在b上方(a先执行) | 正常运行 |
异常 | 正常 | 正常 | a在b上方(a先执行) | 全部回滚 |
正常 | 异常 | 正常 | a在b上方(a先执行) | a不回滚,b,c回滚 |
异常 | 异常 | 正常 | a在b上方(a先执行) | 全部回滚 |
正常 | 正常 | 异常 | a在b上方(a先执行) | a不回滚,b,c回滚 |
正常 | 正常 | 正常 | b在a上方(b先执行) | 正常运行 |
异常 | 正常 | 正常 | b在a上方(b先执行) | 全部回滚 |
正常 | 异常 | 正常 | b在a上方(b先执行) | a不回滚,b,c回滚 |
异常 | 异常 | 正常 | b在a上方(b先执行) | 全部回滚 |
正常 | 正常 | 异常 | b在a上方(b先执行) | a不回滚,b,c回滚 |
由此可见,a是新启动事务,b是直接入c的事务中,a回不回滚和a,b的先后顺序无关。a是新启动的事务,不会被其他事务影响。但是a会影响c的事务。
由表中知道与顺序无关,只演示一种顺序:
-
事务方法c(正常)调用方法a(正常)和方法b(正常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED) b(){}; //事务方法c @Transactiona c(){ a();//正常 b();//正常 }
结果:正常运行
-
事务方法c(正常)调用方法a(异常)和方法b(正常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED) b(){}; //事务方法c @Transactiona c(){ a();//异常 b();//正常 }
结果:全部回滚
-
事务方法c(正常)调用方法a(正常)和方法b(异常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED) b(){}; //事务方法c @Transactiona c(){ a();//正常 b();//异常 }
结果:b,c回滚,a不回滚
-
事务方法c(正常)调用方法a(异常)和方法b(异常)
@Transactional(propagation = Propagation.REQUIRED_NEW) a(){}; @Transactional(propagation = Propagation.REQUIRED) b(){}; //事务方法c @Transactiona c(){ a();//正常 b();//异常 }
结果:全部回滚
-
事务方法c(异常)调用方法a(正常)和方法b(正常)
@Transactional(propagation = Propagation.REQUIRED_NEW)
a(){};
@Transactional(propagation = Propagation.REQUIRED)
b(){};
//事务方法c
@Transactiona
c(){
a();//正常
b();//正常
int i=1/0;
}
结果:b,c回滚,a不回滚。
总结:a是新启动的事务,b会加入c中的事务,a事务启动时,c事务将会挂起。a事务不会收c事务影响,但是c事务会受到a事务的影响,
3、方法a设置为REQUIRED_NEW,方法b都设置为:REQUIRED_,c调用a和b。进行异常捕获
- a异常,在c中捕获–a回滚,b,c不会回滚
- a异常,在a中捕获–a回滚,b,c不会回滚
- c异常,在c中捕获–a回滚,b,c回滚
4、SUPPROTS属性
- 使用: @Transactional(propagation = Propagation.SUPPROTS)
- 描述:如果有事务在运行,当前的方法在该事务中运行,否则,它可以不运行在事务中。
- 比较容易理解
1、方法a设置为REQUIRED,方法b都设置为:SUPPORTS,事务c调用a和b
- 因为c是事务,则a和b一起运行在c的事务中,重要其中一个错误,全部回滚。
2、方法a设置为REQUIRED,方法b都设置为:SUPPORTS,普通c调用a和b
- 由于c是普通方法,则a会新启动事务,a,c没有运行在事务中。a不受b,c影响,但是b,c受各自的顺序和a位置的影响。
5、NOT_SUPPROTES属性
- 使用: @Transactional(propagation = Propagation.NOT_SUPPROTES)
- 描述:当前方法不应该运行在事务中,如果有运行的事务,则将他挂起
- 比较容易理解
6、NAVER属性
- 使用: @Transactional(propagation = Propagation.NAVER)
- 描述:当前方法不应该运行在事务中,如果运行在事务中,则抛出异常
- 比较容易理解,一般与NOT_SUPPROTES属性作比较
7、MANDAFORY属性
- 使用: @Transactional(propagation =MANDAFORY)
- 描述:当前方法必须运行在事务中,如果没有运行的事务,就抛出异常。
- 比较容易理解
8、NESTED属性
- 使用: @Transactional(propagation =NESTED)
- 描述:如果有事务在运行,当前方法就在该事务的嵌套事务内运行,否则就启动一个新事物,并在它自己的事务内云进行
- 理解:如果外层方法中有事务,那么直接创建一个保存点,如果外层方法没有事务,那么就创建一个新的事务,后续操作中如果没有异常情况,那么会清除保存点信息,并且在外层事务中进行提交操作,如果内层方法中存在异常情况,那么会回滚到保存点,外层方法事务会直接进行回滚,如果外层方法中存在异常情况,那么会内层方法会正常执行,并且执行完毕之后释放保存点,并且外层方法事务会进行回滚
1.法a设置为NESTED,方法b都设置为:REQUIRED,方法c是事务方法。方法c调用方法a和方法b
@Transactional(propagation = Propagation.NESTED)
a(){};
@Transactional(propagation = Propagation.REQUIRED)
b(){};
//事务方法c
@Transactiona
c(){
a();
b();
//int i=1/0;
}
方法a | 方法b | 方法c | 结果 |
---|---|---|---|
正常 | 正常 | 正常 | 正常运行 |
异常 | 正常 | 正常 | 全部回滚 |
正常 | 异常 | 正常 | 全部回滚 |
异常 | 异常 | 正常 | 全部回滚 |
正常 | 正常 | 异常 | 全部回滚 |
2、方法a设置为REQUIRED_NEW,方法b都设置为:REQUIRED_,事务c调用a和b。进行异常捕获
- a异常,b正常,在c中捕获a—a回滚,b不回滚。
- a,b正常,c中异常并捕获—全部回滚
9总结:
1、事务传播级别是REQUIRED,当checkout()被调用时(假定被另一类中commit()调用),如果checkout()中的代码抛出异常,即便被捕获,commit()中的其他代码都会roll back;
2、是REQUIRES_NEW,如果checkout()中的代码抛出异常,并且被捕获,commit()中的其他代码不会roll back;如果commit()中的其他代码抛出异常,而且没有捕获,不会导致checkout()回滚;
3、是NESTED,如果checkout()中的代码抛出异常,并且被捕获,commit()中的其他代码不会roll back;如果commit()中的其他代码抛出异常,而且没有捕获,会导致checkout()回滚,
4、PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
5、另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
6、由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.