当前位置: 首页 > 知识库问答 >
问题:

为什么声明的异常被视为意外?

袁何平
2023-03-14

使用Spring框架实现以下场景

@Transactional
processRequest() {
    createOrder();
}

@Transactional
createOrder() {
    ...
    try {
        saveRow();
    } catch (SaveNotAllowedException e) {
        // Handle the expected problem
        ...
        log.info("Save was not allowed...");

        // We have to do this otherwise we get UnexpectedRollbackException
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

@Transactional(rollbackFor = SaveNotAllowedException.class)
saveRow() throws SaveNotAllowedException {
    // Updating row in database is not allowed because of some business condition
    throw new SaveNotAllowedException();
}

请注意:方法是公共的,在不同的类中。省略了参数和不相关的东西。SaveNotAllowedException被选中异常。

在saveRow方法上,我声明我希望对检查的异常进行回滚。在createOrder方法中,我捕获该异常并执行相关工作来处理该情况。但由于这是一个预期的回滚,我希望Spring也将其视为预期的回滚,并让我尝试一下。

有些人认为这是一个特例。据我理解,这是完全相反的,这实际上是通常的情况。拥有业务代码,调用某个方法,抛出业务异常,并检查它,以预期的方式改变行为,这在我看来是相当标准的。那么,为什么我必须执行一个特殊步骤,调用晦涩的API setRollbackOnly()来避免崩溃呢?

共有1个答案

毋承基
2023-03-14

预期的异常被视为意外,因为AOP代理只有在从最后一个@transactional方法返回时才会做出决定,该方法创建了事务。代理必须看到正在传播的异常并为其声明了回滚。

为了防止ExpectiontedrollbackException,检查的异常必须在事务之外的代码中捕获,即在代理关闭事务后调用的代码中捕获。

在最初的问题示例中,事务在processRequest()上启动,它是从框架调用的,显然不能抛出任何检查过的异常。因此,在这种情况下,必须在之前捕获异常。

@Transactional
processRequest() {
    ...
    try {
      createOrder();
    } catch (SaveNotAllowedException e) {
        // Handle the expected problem
        ...
        log.info("Save was not allowed...");
    }

    try {
      backupOrder();
    } catch (AnotherException e) {
        ...
    }       
}

@Transactional(propagation = Propagation.REQUIRES_NEW, 
rollbackFor = SaveNotAllowedException.class)
createOrder() throws SaveNotAllowedException {
    ...
    saveRow();
}

@Transactional(propagation = Propagation.REQUIRES_NEW, 
rollbackFor = AnotherException.class)
backupOrder() throws AnotherException {
    ...
}

@Transactional
saveRow() throws SaveNotAllowedException {
    // Updating row in database is not allowed because of some business condition
    throw new SaveNotAllowedException();
}

从processRequest()中删除@transaction。使用@transactional(rollbackFor=savenotallowedexception.class)在createorder()上声明回滚。在processRequest()中捕获异常。

processRequest() {
    ...
    try {
      createOrder();
    } catch (SaveNotAllowedException e) {
        // Handle the expected problem
        ...
        log.info("Save was not allowed...");
    }

    try {
      backupOrder();
    } catch (AnotherException e) {
        ...
    }       
}

@Transactional(rollbackFor = SaveNotAllowedException.class)
createOrder() throws SaveNotAllowedException {
    ...
    saveRow();
}

@Transactional(rollbackFor = AnotherException.class)
backupOrder() throws AnotherException {
    ...
}

@Transactional
saveRow() throws SaveNotAllowedException {
    // Updating row in database is not allowed because of some business condition
    throw new SaveNotAllowedException();
}

但是解决方案1和2可能不是你想要的。您可能希望将整个流程(createOrder()和backupOrder())放在一个事务中,而不是创建新的流程。另一种方法是创建另一个从processRequest()调用的事务性助手服务方法,该方法将在一个事务中调用createOrder和backupOrder。我们需要创建的helper方法与我们拥有的事务组合一样多。

令人遗憾的是,Spring文档中没有提到这些复杂的细节。最接近的信息是http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/transaction.html#tx-propagation--这是必需的,但需要更详细的说明或至少需要重新措辞。

 类似资料:
  • 问题内容: 为什么即使该类中没有抽象方法,也将类声明为抽象? 问题答案: 这是因为它遵循模板方法设计模式。这些方法具有返回HTTP 405 未实现方法 错误的所有默认行为。如果所有这些方法都是抽象的,那么即使您的业务需求根本不需要它们,也将不得不覆盖所有这些方法。这只会导致样板代码和不确定/不直观的行为。

  • 我正在讨论以下问题:使用Java 8的可选和stream::FlatMap并希望向自定义的添加一个方法,然后检查它是否工作。 更准确地说,我希望向我的添加一个。如果不存在值,则返回一个空流,如果存在,则返回一个包含单个元素的流。 但是,我得出的结论是,声明为final。 为什么会这样呢?有一些类没有声明为final,我个人认为这里没有理由声明final。 作为第二个问题,为什么不是所有的方法都是f

  • 问题内容: 为什么将Java常量声明为static? 在此我了解使用final吗?购买为什么它必须是静态的?为什么它应该是类变量,而不是实例变量? 问题答案: 如果它可以随类的实例而变化,那么显然它不是 常数 。为的每个实例获得不同的pi值意味着什么(甚至不允许构造实例)?还是每个实例的大小写不区分大小写?

  • 变量被编译器声明为const,导致无法编译

  • 我的RMI服务器接口声明了一个方法foo(),该方法被声明为引发RemoteException和Exception,如下所示: 服务器实现为: 我的客户端在服务器上调用foo: 现在,当我运行客户端时,我得到: 从java类型的foo()中获取异常。rmi。异常异常:未声明的检查异常;嵌套的例外是:java。伊奥。InterruptedIOException:操作超时 Java文档是这样说的。rm

  • 问题内容: 将接口声明为抽象有什么意义?接口方法也是如此。有什么意义吗? 例如。 问题答案: 您在哪里遇到过已发布的代码块,任何旧的Java代码库? 这就是 JLS不得不说: 9.1.1.1抽象接口: 每个接口都是隐式抽象的。该修饰符已过时,不应在新程序中使用。 9.4抽象方法声明: 为了与Java平台的较早版本兼容,出于风格考虑,允许但不鼓励为接口中声明的方法冗余地指定abstract修饰符。