Axon 框架:处理状态存储聚合中的数据库异常

问题描述

我是 Axon 框架的新手,正在尝试使用带有状态存储聚合的 CQRS 来实现应用程序。应用程序依赖于数据库约束(目前使用 H2)来强制名称属性的唯一性。我想捕获此异常并将其作为用户友好的域异常重新抛出。

根据 Axon 文档:

但我无法让它发挥作用。我尝试添加异常处理程序如下:

    @ExceptionHandler
    public void handle(ConstraintViolationException ex) throws Exception {
        if (ex.getMessage().contains("UNQ_COMPANY_ID") || ex.getMessage().contains("UNQ_PLAN_NAME")) {
            throw new DomainException("Plan name and company id must be unique");
        }
        throw ex;
    }

但是这个方法没有被调用。我尝试将异常处理程序方法放在聚合和单独的命令处理程序类上,尝试添加 resultType=ConstraintViolationException.class,并尝试捕获其他类型的异常,包括 ExceptionRuntimeException、{{1 }} 等,但从未调用方法

我可以在日志输出中看到错误

org.axonframework.axonserver.connector.command.AxonServerRemoteCommandHandlingException:远程消息处理组件抛出异常:org.hibernate.exception.ConstraintViolationException:无法执行语句

是否可以在状态存储的聚合中捕获数据库异常?如果是这样,有人可以指出我做错了什么吗?

声明“@ExceptionHandler 将只处理从同一类中的消息处理函数抛出的异常”让我怀疑是否需要创建自定义存储库类(而不是使用认的 AxonServerRemoteCommandHandlingException),但这似乎喜欢做很多不必要的工作。

谢谢!


更新:通过向 GenericJpaRepository 方法添加 UnitOfWork 参数并使用它在其上注册回滚回调,我能够大致完成我想要的,如下所示:

@CommandHandler

但这似乎有点冗长,并且限制了我只能抛出未经检查的异常。但这也不是正确的方法,因为我认为 uow.onRollback(unit -> { DefaultUnitOfWork duow = (DefaultUnitOfWork) unit; Throwable ex = duow.getExecutionResult().getExceptionResult(); while (ex != null) { if (ex.getMessage().contains("UNQ_PLAN_NAME")) { throw new RuntimeException("Plan name must be unique"); } ex = ex.getCause(); } }); 注释的目的是消除对上述代码的需求。

解决方法

这当然是可行的。

实际上,如果您可以在 code-samples 存储库中查看有关分布式异常的示例,我可以给您最好的指示。

通常,正如您在共享日志中看到的那样,“原始”异常被包装到 AxonServerRemoteCommandHandlingException 中,这意味着您必须处理它。这样做,您几乎可以向此类的 details 字段添加任何内容,例如添加您有 ConstraintViolationException 的指示(或 ERROR_CODE,如 HTTP 协议),并且您可以在另一边打开它。

,

您需要的“问题”可能是知道 @ExceptionHandler 注释的方法应该驻留在处理消息的对象中。因此,如果您想对失败的命令处理操作做出反应(在您的示例中就是这种情况),您需要将异常处理程序放置在命令处理程序旁边的聚合中。

你得到一个 AxonServerRemoteCommandHandlingException 的事实表明异常是在命令调度端捕获的。因此,在 CommandGateway/CommandBus 上调度命令之前。

然而,这是否是手头的问题,我现在不清楚,因为示例只显示异常处理程序而不是它所在的位置。请分享我对异常处理程序位置的假设是否正确。如果没有,我们将深入研究以找出原因。