Spring事务传播机制:
REQUIRED(默认的):
如果当前没有事务,则自己新建一个事务,并且传播给方法中调用的其他方法,如果已经存在事务,则加入此事务。
举例:
A方法中包含B、C、D方法,B使用REQUIRED,则B方法新建一个事务,
调用A方法时,只有B方法受到事务支持。
如A、B、C、D方法中,只有A方法使用REQUIRED,则ABCD中,B、C、D在被A方法调用时都受事务影响,
但是单独运行B、C、D方法时,因为B、C、D并未加上REQUIRED注解,所以不受事务影响。
适用于增、删、改操作
SUPPORTS:如果当前有事务,则加入此事务,如果没有,则不开启事务。
适用于查询。
MANDATORY:要求调用此方法的方法必须存在事务,否则直接抛出异常。
REQUIRES_NEW:不论当前是否存在事务,都重新开启一个单独的事务
NOT_SUPPORTED:不论当前是否存在事务,我都不支持事务
NEVER:不论当前是否存在事务,我都不支持事务,并且我会抛出一个异常。
NESTED:
如果当前存在事务,则我另开一个子事务,
当主事务回滚时,子事务也会回滚
当子事务回滚时,主事务可以选择回滚或不回滚(通过显式try-catch让主事务不回滚)
当子事务正常提交时,子事务之前的主事务代码不会进行一并提交,
当主事务正常提交时,子事务必定正常提交。
场景 | NESTED | REQUIRES_NEW |
---|---|---|
当存在父级事务时 | 开启一个子事务 | 开启一个独立的事务 |
当父级事务回滚时 | 也会回滚 | 也会回滚 |
当子级事务回滚时 | 父级事务可以选择回滚或不回滚 | 父级事务不会回滚 |
当不存在父级事务时 | 同REQUIRED | 同REQUIRED |
tip:当你对事务传播机制总是似懂非懂时,可以简单理解一个事务就相当于新开了一个try catch块,而"加入"这个说法是片面的,当A方法被开启事务时,如果会传播给A方法中被调用的方法,一般定义为加入此方法的事务,但实际上被调用的方法部分情况下依然会开启一个try块,只不过大部分情况是已经在try-catch块中就不另外开启,所以定义为加入此方法的事务。
难点代码模拟
此部分代码并非实际源码,仅用于快速理解不同的事务传播机制
REQUIRED
package com.test;
import org.junit.Test;
public class REQUIRES {
//REQUIRED情况下 最外层被REQUIRED修饰的方法会展开一个try-catch块,具体如下
//try块里的内容原方法内容
//通过此方法还原被REQUIRED修饰时的场景如下:
// @Transactional(propagation = Propagation.REQUIRED)
// public void REQUIRES {
// new REQUIRES_TEST().ExceptionTest();
// }
//因为此方法内任意操作都会引发最外层的catch
//运行结果:错误影响到了外层哦!
@Test
public void REQUIRES(){
try {
new REQUIRES_TEST().ExceptionTest();
}catch (Exception e){
System.out.println("错误影响到了外层哦!");
}
}
public void ExceptionTest(){
int i = 1/0;
}
}
MANDATORY
package com.test;
import org.junit.Test;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class MANDATORY_TEST {
//MANDATORY_FATHER MANDATORY_GRANDFATHER 任意一层开启事务即可
@Transactional(propagation = Propagation.MANDATORY)
public void MANDATORY(){
System.out.println("多层调用任意一层开启事务就可以了哦!");
}
@Transactional(propagation = Propagation.REQUIRED)
public void MANDATORY_FATHER(){
new MANDATORY_TEST().MANDATORY();
}
@Test
public void MANDATORY_GRANDFATHER(){
new MANDATORY_TEST().MANDATORY_FATHER();
}
}
REQUIRES_NEW
package com.test;
import org.junit.Test;
public class REQUIRES_NEW_TEST {
// 这里可以很明显看出REQUIRES_NEW的作用
// 即REQUIRES_NEW会不论是否已有事务,都新开一个try-catch块
// 所以就算出现错误也只影响自己的catch进行事务回滚
@Test
public void REQUIRES_NEW(){
try {
//REQUIRES_NEW修饰方法前的其他方法
//..............................
//执行到了REQUIRES_NEW修饰的方法
try {
int i = 1 / 0;
} catch (Exception e) {
System.out.println("错误只影响到了内层哦!");
}
}catch (Exception e){
System.out.println("错误影响到了外层哦!");
}
}
public void ExceptionTest(){
int i = 1/0;
}
}
NOT_SUPPORTED
package com.test;
import org.junit.Test;
public class NOT_SUPPORTED_TEST {
@Test
public void REQUIRED(){
try {
//其他代码
//............
//NOT_SUPPORTED修饰的方法
try {
int i = 1 / 0;
}catch (Exception e){
System.out.println("我之前说啥来着?想让我回滚?我偏不!");
}
int i = 1/0;
System.out.println("wc,我自己出问题回滚也影响不到你NOT_SUPPORTED? NOT_SUPPORTED:那当然!");
}catch (Exception e){
//回滚
}
}
public void NOT_SUPPORTED(){
int i = 1/0;
}
}
最后更新于2021年1月14日
相关文章:
为什么SpringBoot中不需要使用@EnableTransactionManagement就能使用事务?
原创不易,如果该文章对你有所帮助,望左上角点击关注~如有任何技术相关问题,可通过评论或直接私信联系我讨论,我会在力所能及之内进行相应回复以及开单章解决该问题.
该文章如有任何错误请在评论中指出,感激不尽,转载请附出处!
个人博客首页:https://blog.csdn.net/yjrguxing ——您的每个关注和评论都对我意义重大