【问题标题】:RuntimeException in child function should not affect parent calling function - does REQUIRES_NEW play a role?子函数中的 RuntimeException 不应影响父调用函数 - REQUIRES_NEW 是否起作用?
【发布时间】:2020-07-14 01:27:45
【问题描述】:

我正在使用 DB2 数据库并测试了以下代码: 无论methodB是否有Propagation.REQUIRES_NEW,如果methodB有异常,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事务注解的普通函数。

问题出在方法A中,我从super.methodB()的ClassB的子类中调用了methodB,并认为它会给出一个事务方法B,但它不起作用:

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

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

不知道super.methodB() 也会因为同样的原因而失败(任何人都可以提供更多解释吗?)


总之,在第一块代码的例子中,当methodB出现RuntimeException时,

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

如果methodB有REQUIRED注解:A&B共享同一个事务; methodA 会回滚

如果methodB有REQUIRES_NEW注解:A&B有单独的事务; methodA 不会回滚

【问题讨论】:

    标签: java transactions spring-transactions


    【解决方案1】:

    没有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();
    }
    

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

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

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

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

    【讨论】:

    • 谢谢@Kayaman。这也是我的理解,但我的测试结果并不能证明这一点。这就是我发布问题的原因。也许我在我的代码中遗漏了某事,将再次检查
    • @wayne 如果您的代码中没有这种行为,那么在调用methodB() 时可能没有考虑@Transactional 注释。一个简单的例子是做new ClassB().methodB();,这样方法调用就不会被拦截,注释也不会被处理。
    • 谢谢@Kayaman。我对此很清楚。我看到的奇怪行为是即使 methodB 没有 requires_new 属性,methodA 也正确提交。只是想知道是否有办法检查当前事务是否标记为回滚?
    • 这既不正常,也不符合规范。您使用的是哪个数据库?
    • IBM DB2 数据库
    猜你喜欢
    • 2019-09-21
    • 2014-05-06
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多