Spring @Transactional事务传播行为详解

 

目录

一、无事务情况

二、有事务情况

REQUIRED

SUPPORTS

MANDATORY

REQUIRES_NEW

NOT_SUPPORTED

NEVER

NESTED


Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。

在复杂的业务场景中,多个事务方法之间的调用可能会导致事务的一致性,如出现数据丢失、重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性。

Spring的事务规定了7种事务的传播级别,默认的传播机制是REQUIRED

一、无事务情况

我们先看一段代码:

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:如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行。

举例:领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。

比如我们代码如下;

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:有事务则加入事务,没有事务则普通执行。

举例:领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。

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:强制有事务,没有事务则报异常。

举例:领导必须管饭,不管饭我就抛出异常。

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:每次执行新开一个事务,如果当前存在事务,则把当前事务挂起。

举例:领导管饭,但我偏不要,我自己买了自己吃,如果领导不管饭,我自己也有钱买饭。

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);
}

结果说明,子方法自己创建了一个新的事务自己用。

如果我父方法上加了事务,这时数据库会发现什么数据都没有。

但我们再考虑一种情况,代码如下:

@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:如果当前有事务,则把事务挂起,自己不使用事务去运行数据库操作。

举例:领导管饭,分一点给你,但你太忙了,放一边不吃。

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);
}

看的出来,压根没使用事务。

当我在父方法上加上事务。

@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:如果当前有事务存在,则抛出异常。

举例:领导管饭,我不想吃,我抛出异常。

@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。但是如果主事务提交,则会携带子事务一起提交。

如果主事务回滚,则子事务一起回滚,相反,子事务异常,则父事务可以回滚或不回滚。

举例:领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任,也可以承担责任。

@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);
}

结果数据库什么数据都没有。因为父有事务,当父方法有异常,里面的嵌套事务要被回滚。

但如果子事务发生异常呢?

@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);
}

结果数据库还是什么数据都没有,这是因为子事务的异常会影响到父方法。但这个是可选的,我们可以给父方法调用加异常,这样就不会影响父方法事务了,如下:

@Transactional(propagation = Propagation.REQUIRES)
public void testPropagationTrans() {
	stuService.saveParent();
	try {
		stuService.saveChildren();
	}catch (Exception e) {
		e.printStackTrace();
	}
}
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神农尝百码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值