警告
本文最后更新于 2020-12-05,文中内容可能已过时。
spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework 。
链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。
1 基础概念
spring声明式事务的传播行为,严格来说是spring特有的,数据库本身并没有这个概念。本文会列举出spring事务的七种传播行为,并以代码案例的方式展示不同的传播行为的表现是怎样的。
1.1 什么是事务传播行为
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法时事务如何传播。
伪代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class A{
@AutoWired
private B b;
public void a(){
b.b();
// 业务代码...
}
}
public class B{
@AutoWired
private A a;
@Transaction(Propagation=XXX)
public void b(){
// 业务代码...
}
}
|
上面代码中 a() 嵌套了 b() 。b() 的事务传播行为由 @Transaction(Propagation=XXX) 决定。注意,a() 并没有开启事务。某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。
1.2 Spring声明式事务中七种传播行为
传播类型 |
说明 |
REQUIRED |
如果当前没有事务,就新建一个事务;如果当前存在事务,就加入到当前事务。 |
SUPPORTS |
如果当前没有事务,就以非事务方式运行;如果当前存在事务,就加入到当前事务。 |
MANDATORY |
如果当前没有事务,就抛出异常;如果当前存在事务,就加入到当前事务。 |
REQUIRES_NEW |
总是在新建事务中运行。如果当前已存在事务,就挂起当前事务。 |
NOT_SUPPORTED |
以非事务方式运行;如果当前存在事务,挂起当前事务。 |
NEVER |
以非事务的方式运行。如果当前存在事务,就抛出异常。 |
NESTED |
如果当前存在事务,则在嵌套事务内运行;如果当前不存在事务,则新建一个事务(这时与 REQUIRED 类似)。 |
2 基础代码准备
文中代码以 Service 和 Dao 两层展示。使用测试用例调用 Service 的方式,框架采用 spring + mybatis ,数据库 mysql。dao 层及实体类代码使用插件生成。
自动代理激活及扫描包配置类 AspectAnoForTransactionConfiguration :
1
2
3
4
5
6
|
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"top.wlz922.service"})
public class AspectAnoForTransactionConfiguration {
}
|
事务配置类:
1
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
30
31
32
33
34
35
|
@Configuration
@MapperScan(basePackages = "top.wlz922.dao")
@EnableTransactionManagement
public class GlobalTransactionConfig {
@Autowired
ApplicationContext context;
@Bean
public DataSourceTransactionManager getTransactionManager(@Autowired DataSource ds){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(ds);
return manager;
}
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource ds) throws IOException {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);
ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(context);
Resource[] resources = resolver.getResources("classpath:sqlmaps/*.xml");
fb.setMapperLocations(resources);
return fb;
}
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUsername("root");
ds.setPassword("123456");
ds.setUrl("jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai");
ds.setDriverClassName("com.mysql.jdbc.Driver");
return ds;
}
}
|
实体类 PropagationUser :
1
2
3
4
5
6
7
8
9
10
|
@Data
@NoArgsConstructor
public class PropagationUser implements Serializable {
private Long id;
private String name;
private static final long serialVersionUID = 1L;
public PropagationUser(String name) {
this.name = name;
}
}
|
PropagationUserService (其实现类的事务注解配置与方法名一致):
1
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
|
public interface PropagationUserService {
void addRequired(PropagationUser user);
void addRequiredException(PropagationUser user);
void addRequiredNew(PropagationUser user);
void addRequiredNewException(PropagationUser user);
void addSupports(PropagationUser user);
void addSupportsException(PropagationUser user);
void addNotSupported(PropagationUser user);
void addNotSupportedException(PropagationUser user);
void addMadatory(PropagationUser user);
void addMadatoryException(PropagationUser user);
void addNever(PropagationUser user);
void addNeverException(PropagationUser user);
void addNestead(PropagationUser user);
void addNesteadException(PropagationUser user);
}
|
PropagationUserDao :
1
2
3
4
|
public interface PropagationUserDao {
int insertSelective(PropagationUser record);
// 省略...
}
|
测试类:
1
2
3
4
5
6
7
8
9
10
|
public class TransactionPropagationServiceTest {
private TransactionPropagationService service;
@Before
public void before() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
GlobalTransactionConfig.class, AspectAnoForTransactionConfiguration.class);
service = context.getBean(TransactionPropagationService.class);
}
}
|
具体的验证代码由 TransactionPropagationService 层实现,根据不同的传播行为分别列举。
3 REQUIRED
3.1 外围方法未开启事务
验证方法 1 :外围方法抛出异常
1
2
3
4
5
6
7
8
9
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionExceptionRequiredRequired() {
userService.addRequired(new PropagationUser("张三"));
userService.addRequired(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2 :内部方法抛出异常
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionRequiredRequiredException() {
userService.addRequired(new PropagationUser("张三"));
userService.addRequiredException(new PropagationUser("李四"));
}
}
|
分别执行验证方法,结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法未开启事务,“张三”、“李四”插入,外围方法异常不影响内部插入,“张三”、“李四”方法在独立的事务中运行。 |
2 |
“张三”插入,“李四”未插入 |
外围方法未开启事务,内部方法异常。“张三”插入,“李四”未插入,说明“张三”、“李四”方法是在不同的事务中运行。 |
结论:通过这两个方法,说明在外围方法不开启事务时,事务设置为 REQUIRED 传播行为的内部方法,都会新建事务。在不同的事务中运行,互不影响。
3.2 外围方法开启事务
验证方法 1 :外围方法抛出异常
1
2
3
4
5
6
7
8
9
10
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionExceptionRequiredRequired() {
userService.addRequired(new PropagationUser("张三"));
userService.addRequired(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2 :内部方法抛出异常
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionRequiredRequiredException() {
userService.addRequired(new PropagationUser("张三"));
userService.addRequiredException(new PropagationUser("李四"));
}
}
|
验证方法 3 :内部方法抛出异常被外围捕获
1
2
3
4
5
6
7
8
9
10
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionRequiredRequiredCatchException() {
userService.addRequired(new PropagationUser("张三"));
try {
userService.addRequiredException(new PropagationUser("李四"));
} catch (Exception ignored) { }
}
}
|
分别执行验证方法,结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法加入外围方法事务。外围方法回滚,内部方法也要回滚。 |
2 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法加入外围方法事务。内部方法抛出异常回滚,外围方法感知到异常。 |
3 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法加入到外围方法事务。内部方法抛出异常,传递到外围,外围方法捕获。由于在同一个事务中,所以总体回滚。 |
结论:以上结果表明,在外围方法开启事务,内部方法事务传播行为使用 REQUIRED 时,会加入到外围方法的事务。任何一个节点方法回滚,都会触发整体事务回滚。因为他们本就在同一个事务中。
4 SUPPORTS
4.1 外围方法未开启事务
验证方法 1:外部方法抛出异常
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionExceptionSupportsSupports() {
userService.addSupports(new PropagationUser("张三"));
userService.addSupports(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:内部方法抛出异常
1
2
3
4
5
6
7
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionSupportsSupportsException() {
userService.addSupports(new PropagationUser("张三"));
userService.addSupportsException(new PropagationUser("李四"));
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法未开启事务,外围方法抛出异常,内部方法均插入成功。说明内部方法要么没有事务,要么在独立的事务中。 |
2 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法抛出异常,内部方法均插入成功。说明内部方法没有开启事务。 |
结论:当外围方法未开启事务时,内部方法事务传播行为使用 SUPPORTS,那么内部方法会以非事务的方式运行。
4.2 外围方法开启事务
验证方法 1:外部方法抛出异常
1
2
3
4
5
6
7
8
9
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionExceptionSupportsSupports() {
userService.addSupports(new PropagationUser("张三"));
userService.addSupports(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:内部方法抛出异常
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionSupportsSupportsException() {
userService.addSupports(new PropagationUser("张三"));
userService.addSupportsException(new PropagationUser("李四"));
}
}
|
验证方法 3:内部方法抛出异常被外围方法捕获
1
2
3
4
5
6
7
8
9
10
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionSupportsSupportsCatchException() {
userService.addRequired(new PropagationUser("张三"));
try {
userService.addRequiredException(new PropagationUser("李四"));
} catch (Exception ignored) { }
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法加入到外围方法事务,外围方法抛出异常。事务回滚,内部方法插入数据均回滚。 |
2 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法加入到外围方法事务,内部方法抛出异常,外围方法感知。事务回滚。 |
3 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法加入到外围方法事务,内部方法抛出异常回滚,外围方法捕获。由于在同一个事务中,所以整体回滚。 |
结论:当外围方法开启事务,内部方法事务传播行为使用 SUPPORTS 时,会加入到外围方法的事务中,外围、内部方法逻辑代码,实际上是在一个事务内执行,任何一个方法抛出异常,都会回滚。
5 MANDATORY
验证方法 1:
1
2
3
4
5
6
7
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionMandatoryMandatory() {
userService.addMandatory(new PropagationUser("张三"));
userService.addMandatory(new PropagationUser("李四"));
}
}
|
验证方法 2:
1
2
3
4
5
6
7
8
9
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionExceptionMandatoryMandatory() {
userService.addMandatory(new PropagationUser("张三"));
userService.addMandatory(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 3:
1
2
3
4
5
6
7
8
9
10
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionMandatoryMandatoryCatchException() {
userService.addMandatory(new PropagationUser("张三"));
try {
userService.addMandatoryException(new PropagationUser("李四"));
} catch (Exception ignored) { }
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
”张三“未插入,”李四“未插入,内部方法未执行,抛出IllegalTransactionStateException |
外围方法未开启事务,内部方法在调用前就抛出了异常。说明只允许开启了事务的方法调用此方法,否则就会抛出异常。 |
2 |
“张三“未插入,”李四“未插入 |
外围方法开启事务,外围方法抛出异常。由于内部方法加入了外围方法事务,所以全部回滚。 |
3 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法抛出异常。由于内部方法加入了外围方法事务,索引任何一个节点方法异常,都会回滚。 |
结论:
如果外围方法未开启事务,内部方法事务传播行为使用 Mandatory,会在调用被代理方法之前,事务切面抛出异常。
如果外围方法开启事务,内部方法会加入到外围方法的事务中。由于在同一个事务中,任何一个方法节点抛出异常,会全部回滚。
6 REQUIRES_NEW
6.1 外围方法未开启事务
验证方法 1:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionExceptionRequiresNewRequiresNew() {
userService.addRequiredNew(new PropagationUser("张三"));
userService.addRequiredNew(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionRequiresNewRequiresNewException() {
userService.addRequiredNew(new PropagationUser("张三"));
userService.addRequiredNewException(new PropagationUser("李四"));
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法分别新建一个事务。外围方法异常,不影响内部方法独立开启的事务。 |
2 |
“张三”插入,“李四为插入 |
外围方法未开启事务,内部方法分别新建一个事务。由于两个内部方法事务是独立的,互不影响,所以只回滚了一个。 |
结论:外围方法未开启事务,内部方法事务传播行为如果使用 REQUIRES_NEW,那么内部方法总是会创建一个新的事务,在这个新的事务中执行自己的逻辑。
6.2 外围方法开启事务
验证方法 1:
1
2
3
4
5
6
7
8
9
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionExceptionRequiresNewRequiresNew() {
userService.addRequiredNew(new PropagationUser("张三"));
userService.addRequiredNew(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionRequiresNewRequiresNewException() {
userService.addRequiredNew(new PropagationUser("张三"));
userService.addRequiredNewException(new PropagationUser("李四"));
}
}
|
验证方法 3:
1
2
3
4
5
6
7
8
9
10
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionRequiresNewRequiresNewCatchException() {
userService.addRequiredNew(new PropagationUser("张三"));
try {
userService.addRequiredNewException(new PropagationUser("李四"));
} catch (Exception ignored) { }
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,”李四“插入 |
外围方法开启事务,内部方法在新建事务中运行。外围方法异常回滚,不影响内部方法独立的事务。 |
2 |
“张三”插入,“李四”未插入 |
外围方法开启事务,内部方法在新建事务中运行。“李四”方法抛出异常,外围方法感知,两个方法事务回滚。”张三“方法由于在独立的事务中,不受影响。 |
2 |
“张三”插入,“李四”未插入 |
外围方法开启事务,内部方法在新建事务中运行。“李四”方法抛出异常,外围方法捕获,”李四“方法事务回滚。”张三“方法由于在独立的事务中,不受影响。 |
结论:无论外围方法是否开启事务,事务传播行为使用 REQUIRES_NEW 的方法,总是会新启一个事务,在这个新的事务中执行内部逻辑。
7 NOT_SUPPORTED
7.1 外围方法未开启事务
验证方法 1:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionExceptionNotSupportedNotSupported() {
userService.addNotSupported(new PropagationUser("张三"));
userService.addNotSupported(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionNotSupportedNotSupportedException() {
userService.addNotSupported(new PropagationUser("张三"));
userService.addNotSupportedException(new PropagationUser("李四"));
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法均未开启事务,所以外围方法抛出异常,都没有回滚。 |
2 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法均未开启事务,所以内部方法抛出异常,也没有回滚。 |
结论:外围方法未开启事务,内部方法事务传播行为如果使用 NOT_SUPPORTED,后者会在非事务的环境中运行。
7.2 外围方法开启事务
验证方法 1:
1
2
3
4
5
6
7
8
9
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionExceptionNotSupportedNotSupported() {
userService.addNotSupported(new PropagationUser("张三"));
userService.addNotSupported(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionNotSupportedNotSupportedException() {
userService.addNotSupported(new PropagationUser("张三"));
userService.addNotSupportedException(new PropagationUser("李四"));
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法开启事务,内部方法会将事务挂起,以非事务方式运行,所以外围方法回滚,内部方法不回滚。 |
2 |
“张三”插入,“李四”插入 |
外围方法开启事务,内部方法会将事务挂起,以非事务方式运行,所以外围方法回滚,内部方法不回滚。 |
结论:外围方法开启事务,内部方法事务传播行为如果使用 NOT_SUPPORTED,则后者会将外围方法的事务挂起,以非事务的方式运行。
8 NEVER
验证方法 1:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionExceptionNeverNever() {
userService.addNever(new PropagationUser("张三"));
userService.addNever(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionNeverNeverException() {
userService.addNever(new PropagationUser("张三"));
userService.addNeverException(new PropagationUser("李四"));
}
}
|
验证方法 3:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionNeverNever() {
userService.addNever(new PropagationUser("张三"));
userService.addNever(new PropagationUser("李四"));
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法以非事务方式运行,因为根本就没有事务,外围方法抛出异常,也不会回滚。 |
2 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法以非事务方式运行,因为根本就没有事务,内部方法抛出异常,也不会回滚。 |
3 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法未执行,在事务切面中抛出IllegalTransactionStateException ,方法都没有执行,所以数据没有插入. |
结论:
方法的事务传播行为使用 NEVER。外围方法未开启事务时,方法以非事务方式运行。外围方法开启事务时,直接抛出异常。
即这种方式的传播行为,调用者只能是未开启事务的,才可以调用,否则业务方法根本不会执行,直接抛出异常。
9 NESTED
9.1 外围方法未开启事务
验证方法 1:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionExceptionNestedNested() {
userService.addNestead(new PropagationUser("张三"));
userService.addNestead(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Override
public void noTransactionNestedNestedException() {
userService.addNestead(new PropagationUser("张三"));
userService.addNesteadException(new PropagationUser("李四"));
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”插入,“李四”插入 |
外围方法未开启事务,内部方法均开启一个新的事务,所以外围范方法抛出异常不影响两个独立的事务。 |
2 |
“张三”插入,“李四”未插入 |
外围方法未开启事务,内部方法各自开启新的事务,所以“李四”所在事务回滚。而“张三”插入。 |
结论:方法事务传播行为使用 NESTED,如果外围方法未开启事务,那么内部方法会各自开启自己的事务。
9.2 外围方法开启事务
验证方法 1:
1
2
3
4
5
6
7
8
9
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionExceptionNestedNested() {
userService.addNestead(new PropagationUser("张三"));
userService.addNestead(new PropagationUser("李四"));
throw new RuntimeException();
}
}
|
验证方法 2:
1
2
3
4
5
6
7
8
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionNestedNestedException() {
userService.addNestead(new PropagationUser("张三"));
userService.addNesteadException(new PropagationUser("李四"));
}
}
|
验证方法 3:
1
2
3
4
5
6
7
8
9
10
11
|
public class TransactionPropagationServiceImpl implements TransactionPropagationService {
@Transactional(rollbackFor = Exception.class)
@Override
public void transactionNestedNestedCatchException() {
userService.addNestead(new PropagationUser("张三"));
try {
userService.addNesteadException(new PropagationUser("李四"));
} catch (Exception ignore) {
}
}
}
|
验证结果:
验证方法序号 |
执行结果 |
结果分析 |
1 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法会在嵌套事务内执行,外围方法异常回滚,所有的嵌套事务都会回滚。 |
2 |
“张三”未插入,“李四”未插入 |
外围方法开启事务,内部方法会在嵌套事务内执行,“李四”插入抛出异常,外部方法感知异常,全部回滚。 |
3 |
“张三”插入,“李四”未插入 |
外围方法开启事务,内部方法在嵌套事务内执行,“李四”插入抛出异常回滚。外部方法捕获异常未抛出。所以只回滚局部的嵌套事务。 |
结论:一个方法事务传播行为使用 NESTED。外围方法未开启事务时,方法会新启一个事务。在外围方法开启事务时,方法会在嵌套事务内执行。
注意
嵌套事务的特点:如果外围方法异常回滚,所有的嵌套事务都会回滚。如果某些节点的嵌套事务回滚,异常在外围方法中没有继续向上抛出,那么仅仅回滚异常的嵌套事务。
关于嵌套事务的实现原理,在事务源码中会详细列出。不只是嵌套事务,事务的其中传播行为源码实现流程都会详细列出。