事务传播行为指当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播,例如,方法可能继续在当前事务中执行,也可以开启一个新的事务,在自己的事务中执行。
声明式事务的传播行为可以通过 @Transactional 注解中的 propagation 属性来定义。
一、事务传播方式
- PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
二、PROPAGATION_REQUIRES_NEW与PROPAGATION_NESTED的理解
-
PROPAGATION_REQUIRES_NEW: 启动一个新的, 不依赖于环境的 "内部" 事务。 这个事务commit或 rollback 不依赖于外部事务,它拥有自己的隔离范围。当内部事务开始执行时,外部事务将被挂起,内部事务结束时,外部事务将继续执行。
-
内部方法选择REQUIRES_NEW会新开启事务,外层事务不会影响内部事务的提交/回滚
-
内部方法选择REQUIRES_NEW,当内部事务的发生异常,会影响外部事务的回滚
-
-
PROPAGATION_NESTED: 开始一个 "嵌套的" 事务,它是已经存在事务的一个真正的子事务。嵌套事务开始执行时,它将取得一个 savepoint。如果这个嵌套事务失败, 我们将回滚到此savepoint。嵌套事务是外部事务的一部分,只有外部事务结束后它才会被提交。
- 内部事务执行发生异常,外层事务不处理异常,外层事务和内层事务会一起回滚
- 内部事务执行发生异常,外层事务处理异常,外层事务正常提交,但内层事务会回滚
三、REQUIRED、REQUIRES_NEW、NESTED的使用差异
- 定义serviceA.methodA() 以PROPAGATION_REQUIRED修饰;
- 定义serviceB.methodB() 以表格中三种方式修饰;
- methodA中调用methodB
异常状态 | PROPAGATION_REQUIRES_NEW (两个独立事务) |
PROPAGATION_NESTED (B的事务嵌套在A的事务中,事务对象为同一个) |
PROPAGATION_REQUIRED (同一个事务) |
---|---|---|---|
methodA抛异常 methodB正常 |
A回滚,B正常提交 | A与B一起回滚 | A与B一起回滚 |
methodA正常 methodB抛异常 |
1.如果A中捕获B的异常,并没有继续向上抛异常,则B先回滚,A再正常提交; 2.如果A未捕获B的异常,默认则会将B的异常向上抛,则B先回滚,A再回滚 |
1.如果A中捕获B的异常,并没有继续向上抛异常,则B先回滚,A再正常提交; 2.如果A未捕获B的异常,默认则会将B的异常向上抛,则B先回滚,A再回滚 |
A与B一起回滚 |
methodA抛异常 methodB抛异常 |
B先回滚,A再回滚 | A与B一起回滚 | A与B一起回滚 |
methodA正常 methodB正常 |
B先提交,A再提交 | A与B一起提交 | A与B一起提交 |