子函数中的 RuntimeException 不应影响父调用函数 - REQUIRES_NEW 是否起作用?

RuntimeException in child function should not affect parent calling function - does REQUIRES_NEW play a role?

我正在使用 DB2 数据库并测试了以下代码: 不管methodB是否有Propagation.REQUIRES_NEW,如果methodB有exception,methodA的结果无论如何都会正确提交。

这违背了我的假设,即必须使用 Propagation.REQUIRES_NEW 来实现这一点。

        ClassA
            @Autowire
            private ClassB classB;

            @Transactional
            methodA(){
                ...
                try{
                     classB.methodB();
                }catch(RuntimeException ex){
                     handleException(ex);
                }
                ...
            }

        ClassB
            @Transactional(propagation = Propagation.REQUIRES_NEW)
            methodB(){...}

感谢@Kayaman 我想我现在明白了。

我看到的行为是因为 methodB 的 @Transactional 注解没有起作用,所以 methodB 被当作一个没有 any 事务注解的普通函数。

错误的地方在于,在 methodA 中,我通过 super.methodB() 从 ClassB 的子 class 调用了 methodB,并认为它会提供一个事务性 methodB,但它不起作用:

    @Service
    @Primary
    ClassC extends ClassB{
        @override
        methodB(){
            super.methodB();
        }
    }

我知道如果您从另一个非事务方法调用一个事务方法,那么事务注释将不起作用 class。

不知道 super.methodB() 也会因为同样的原因而失败(有人可以给出更多解释吗?)


综上所述,在第一个代码块的例子中,当methodB出现RuntimeException时,

如果方法B有NO transaction注解:A&B共享同一个事务;方法 A 不会回滚

如果方法B有REQUIRED注解:A&B共享同一个事务;方法 A 将回滚

如果方法 B 有 REQUIRES_NEW 注释:A 和 B 有单独的事务;方法 A 不会回滚

没有 REQUIRES_NEW(即默认 REQUIRED 或其他行为类似的方法之一),ClassB.methodB() 参与 相同 交易为 ClassA.methodA()methodB() 中的异常将标记 要回滚的相同 事务。即使捕获到异常,事务也会被回滚。

对于REQUIRES_NEW,回滚的事务将特定于methodB(),因此当您捕获异常时,仍然存在健康的原始非回滚事务。


ClassA
@Transactional
methodA(){
    try{
         classB.methodB();
    }catch(RuntimeException ex){
         handleException(ex);
    }
}

ClassB
@Transactional
methodB(){
    throw new RuntimeException();
}

以上代码将回滚整个事务。对于 methodB()propagation=TransactionPropagation.REQUIRES_NEW 不会。

如果 methodB() 没有 任何 注释,methodA() 级别将只有一个 tx 边界并且 Spring 不会意识到抛出异常,因为它在方法的中间被捕获。这类似于将 methodB() 的内容内联到 methodA()。如果该异常是非数据库异常(例如NullPointerException),事务将正常提交。如果该异常是数据库异常,则 基础数据库事务 将被设置为回滚,但 Spring 并不知道这一点。 Spring 然后尝试提交,并抛出一个 UnexpectedRollbackException,因为数据库不允许提交 tx。

明确或无意地省略注释是错误的。如果您打算执行数据库操作,您必须使用 定义良好的 事务上下文,并且了解您的传播。

调用 super.methodB() 绕过 Spring 的正常代理机制,因此即使有注释,也会被忽略。最后,调用 super.methodB() 对我来说似乎是一种设计味道。使用继承来减少行数通常是不好的做法,在这种情况下会导致严重的错误。