1 | TX {
1.1 | input;
2.2 | database access;
2 | RETRY {
3 | TX {
3.1 | database access;
| }
| }
同样,出于同样的原因,即使 RETRY
(2) 最终成功,内部事务 TX
(3) 也会导致外部事务 TX
(1) 失败。
不幸的是,正如下面的示例所示,同样的影响也会从重试块扩散到周围的重复批处理(如果有的话):
1 | TX {
2 | REPEAT(size=5) {
2.1 | input;
2.2 | database access;
3 | RETRY {
4 | TX {
4.1 | database access;
| }
| }
| }
现在,如果 TX
(3) 回滚,就会污染 TX
(1) 的整个批处理,并迫使它在最后回滚。
非默认传播怎么办?
在前面的示例中,如果两个事务最终都成功了,那么 TX
(3) 的 PROPAGATION_REQUIRES_NEW
可以防止外部 TX
(1) 被污染。但如果 TX
(3) 提交,而 TX
(1) 回滚,则 TX
(3) 保持提交,因此我们违反了 TX
(1) 的事务契约。如果 TX
(3) 回滚,TX
(1) 不一定回滚(但实际上可能回滚,因为重试会抛出回滚异常)。
TX
(3) 中的 PROPAGATION_NESTED
在重试情况下(以及在有跳转的批次中)可以按照我们的要求运行: TX
(3) 可以提交,但随后会被外部事务 TX
(1) 回滚。如果 TX
(3) 回滚,TX
(1) 实际上也会回滚。该选项仅在某些平台上可用,不包括 Hibernate 或 JTA,但它是唯一能持续工作的选项。
上例显示的是一个无状态的 RETRY
(3),在最后一次尝试失败后会启动 RECOVER
(5) 路径。stateless
标签的意思是,在某个限制范围内,重复执行该代码块时不会再抛出任何异常。只有当事务 TX
(4) 嵌套了传播时,这种方法才有效。
如果内部 TX
(4) 具有默认传播属性并回滚,则会污染外部 TX
(1)。事务管理器会认为内部事务已损坏事务资源,因此无法再次使用。
对嵌套传播的支持非常罕见,因此我们选择在当前版本的 Spring Batch 中不支持无状态重试恢复。使用前面所示的典型模式,总能达到相同的效果(代价是重复更多的处理)。