目录
Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。
在复杂的业务场景中,多个事务方法之间的调用可能会导致事务的一致性,如出现数据丢失、重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性。
Spring的事务规定了7种事务的传播级别,默认的传播机制是REQUIRED。
一、无事务情况
我们先看一段代码:
java
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
当我们运行testPropagationTrans方法,发现数据库是这样的
当saveChildren方法中有异常,也不会做事务回滚,其实也正常,因为我们也没加事务。
二、有事务情况
注:我们为了好理解,把testPropagationTrans叫做父方法,saveParent和saveChildren叫做子方法。
REQUIRED
**REQUIRED:**如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行。
**举例:**领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。
比如我们代码如下;
java
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
这样的结果很好理解,我在子方法有事务,当然只管我自己的回滚,并不管其它的,正所谓我有钱我自己买饭吃,别人我不管。
但是当你把@Transactional(propagation = Propagation.REQUIRED)放在父方法上,那么数据库里将会什么数据都没有,因为父方法有事务,子方法会加入此事务,相当于都包在里面了,所以有异常则全部回滚。
SUPPORTS
**SUPPORTS:**有事务则加入事务,没有事务则普通执行。
**举例:**领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。
java
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
很明显,如果父方法没有事务,那就相当于都没事务,就正常运行了。
但如果父方法有@Transactional(propagation = Propagation.REQUIRED)注解,则加入父方法事务。
MANDATORY
**MANDATORY:**强制有事务,没有事务则报异常。
**举例:**领导必须管饭,不管饭我就抛出异常。
java
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.MANDATORY)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
这样系统直接抛出异常,因为父方法没有事务。
REQUIRES_NEW
**REQUIRES_NEW:**每次执行新开一个事务,如果当前存在事务,则把当前事务挂起。
**举例:**领导管饭,但我偏不要,我自己买了自己吃,如果领导不管饭,我自己也有钱买饭。
java
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
结果说明,子方法自己创建了一个新的事务自己用。
如果我父方法上加了事务,这时数据库会发现什么数据都没有。
但我们再考虑一种情况,代码如下:
java
@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
我们把异常代码放在父方法中,这时数据库结果发现:
这个结果是如何造成的呢?
其实当执行代码时,子方法创建了一个新的事务,并把父方法的事务挂起,所以也就是说,子事务是子事务,父事务是父事务,这是两个事务。
所以当父方法有异常时,直接回滚saveParent方法,并不影响子事务的操作。
NOT_SUPPORTED
**NOT_SUPPORTED:**如果当前有事务,则把事务挂起,自己不使用事务去运行数据库操作。
**举例:**领导管饭,分一点给你,但你太忙了,放一边不吃。
java
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
看的出来,压根没使用事务。
当我在父方法上加上事务。
java
@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
这个结果说明子方法没有事务,而且还把父事务挂起,所以父有事务,导致saveParent方法可以回滚。
NEVER
**NEVER:**如果当前有事务存在,则抛出异常。
**举例:**领导管饭,我不想吃,我抛出异常。
java
@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NEVER)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
代码直接报错异常,因为never不允许你(父)有事务。
NESTED
**NESTED:**如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同REQUIRED。但是如果主事务提交,则会携带子事务一起提交。
如果主事务回滚,则子事务一起回滚,相反,子事务异常,则父事务可以回滚或不回滚。
**举例:**领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任,也可以承担责任。
java
@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
结果数据库什么数据都没有。因为父有事务,当父方法有异常,里面的嵌套事务要被回滚。
但如果子事务发生异常呢?
java
@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(30);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
结果数据库还是什么数据都没有,这是因为子事务的异常会影响到父方法。但这个是可选的,我们可以给父方法调用加异常,这样就不会影响父方法事务了,如下:
java
@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
stuService.saveParent();
try {
stuService.saveChildren();
}catch (Exception e) {
e.printStackTrace();
}
}