# 详解事务的传播
在前面演示中,我们使用了这个注解
@Transactional(propagation = Propagation.REQUIRED)
查看 Transactional 的源码可以看到它有一个默认的事物传播类型
Propagation propagation() default Propagation.REQUIRED;
Propagation 就是支持的事物传播类型了,定义了 7 个枚举,下面分别来讲解
REQUIRED:支持当前事务,如果不存在则创建一个新事务 这是事务注释的默认设置
SUPPORTS:支持当前事务,如果不存在则非事务执行
主要用于查询操作,因为查询也有可能异常,所以如果调用它的方法已经存在一个事物,那么我们就加入这个事物,如果不存在事物,则以非事物方式执行。
MANDATORY:支持当前事务,如果不存在则抛出异常
REQUIRES_NEW:创建一个新的事务,并暂停当前事务(如果存在)
// REQUIRED parent { s1(); // REQUIRED s2(); // REQUIRES_NEW,并且无异常 // 模拟一个异常 }
1
2
3
4
5
6方法调用之后:
- 尽管 parent 方法抛出异常了,但是 s2 是不会产于 parent 的事物的,所以 s2 的保存生效了
- s1 保存失效,原因是因为它支持 parent 的事物,parent 由于异常会触发当前事物回滚
// REQUIRED parent { s1(); // REQUIRED try{ s2(); // REQUIRES_NEW, 模拟一个异常 }cace(e) }
1
2
3
4
5
6
7s1 保存成功,s2 保存失败,由于 REQUIRES_NEW 是新创建的事物,不是和 parent 使用的同一个事物,所以只会影响它自己;
// REQUIRED parent { s1(); // REQUIRED try{ s2(); // REQUIRED, 模拟一个异常 }cace(e) }
1
2
3
4
5
6
7这样的话,事物就会报错了,因为用的是同一个事物,s2 异常之后,就意味着该事物会回滚;
总结如下:
- 如果当前有事物,则挂起该事物,并且创建一个新的事物给自己使用
- 如果没有事物,则同 REQUIRED
- 父事务有异常,不影响子
NOT_SUPPORTED:以非事务方式执行,如果存在当前事务,则挂起当前事务
挂起的意思和上面 暂停事物 类似
// REQUIRED parent { s1(); // REQUIRED s2(){ sc1() // 模拟一个异常 sc2() } // NOT_SUPPORTED }
1
2
3
4
5
6
7
8
9方法调用后:
s2 中不会参加 parent 的事物,所以 sc1 保存成功,sc2 由于异常原因不会执行
s1 属于会参与 parent 的事物,但是在调用 s2 时抛出了异常,那么 s1 会回滚
NEVER:非事务执行,如果存在事务,则引发异常
NESTED:如果当前事务存在,则在嵌套事务中执行,否则表现为 REQUIRED
嵌套事物和 REQUIRES_NEW 类似,如果当前存事物,他们都会新建一个事物执行
// REQUIRED parent { s1(); // REQUIRED s2(){ sc1() sc2() } // NESTED 无异常 // 模拟异常 }
1
2
3
4
5
6
7
8
9这个表现就是 s2 使用了 parent 的事物
// REQUIRED parent { s1(); // REQUIRED s2(){ sc1() // 模拟异常 sc2() } // NESTED }
1
2
3
4
5
6
7
8
9同理, s2 使用了 parent 的事物
// REQUIRED parent { s1(); // REQUIRED try{ // 在数据库中有一个概念是 save point s2(){ sc1() // 模拟异常 sc2() } // NESTED }cace(){} }
1
2
3
4
5
6
7
8
9
10
11
12
13当 s2 异常后,s2 的执行会回滚,但是不会影响 parent 里面的 s1 保存。
总结如下:
- 如果当前有事务:则开启子事务(嵌套事务),嵌套事务是独立提交或则回滚
- 如果当前没有事物,则同 REQUIRED
- 但是如果主事物提交,则会携带子事物一起提交
- 如果主事物回滚,则子事物会一起回滚,相反,子事物异常,则父事务可以选择回滚还是提交
以上事物传播类型的含义,在源码中有写,翻译成中文就是如上所示的含义。
有争议的是 REQUIRES_NEW 和 NESTED:
REQUIRES_NEW,始终启动一个新事物
- 两个事物没有关联
- 外部事物回滚,里面的事物不受影响
NESTED,在原事物内启动一个内嵌事物
- 两个事物有关联
- 外部事物回滚,内嵌事物也会回滚
所以他们的不同点的表现是:外部事物是否会影响内部事物。其他的事物表现都差不多。
如果要测试事物传播类型的话,可以使用 Junit 来测试,当然是要在之前的 stu 测试服务去写测试代码.
在 foodie-dev-api 项目中加入 spring boot 的 Junit 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2
3
4
5
测试类写法为
package cn.mrcode.foodiedev.api;
import cn.mrcode.foodiedev.Application;
import cn.mrcode.foodiedev.service.StuService;
import cn.mrcode.foodiedev.service.impl.TestTransServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TransTest {
@Autowired
private TestTransServiceImpl testTransService;
@Test
public void myTest() {
testTransService.testPropagationTrans();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.mrcode.foodiedev.service.impl;
import cn.mrcode.foodiedev.pojo.Stu;
import cn.mrcode.foodiedev.service.StuService;
import cn.mrcode.foodiedev.service.TestTransService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 测试事物
*/
@Service
public class TestTransServiceImpl implements TestTransService {
@Autowired
private StuService stuService;
// @Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
// 模拟一个异常
int a = 1 / 0;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
我们要来测试最简单的办法就是,执行创建、更新等操作,然后在里面抛出异常,之类的手段进行查看事物是否按照设置的事物传播类型的表现生效。
比如上面这一个,正常调用了新增的两个对象,然后模拟一个异常,在 不加/加 事物注解的情况下执行,观察数据库结果是否一致。正常情况下如下描述:
- 未使用事物注解:执行测试后,数据库中会出现两条数据
- 使用事物注解:执行测试后,数据库中不会出现数据