问题描述
我是 Axon 框架的新手,正在尝试使用带有状态存储聚合的 CQRS 来实现应用程序。应用程序依赖于数据库约束(目前使用 H2)来强制名称属性的唯一性。我想捕获此异常并将其作为用户友好的域异常重新抛出。
根据 Axon 文档:
-
Exception Handling 表示“
@ExceptionHandler
将仅处理从同一类中的消息处理函数抛出的异常” -
Message Intercepting 文档说“用
@ExceptionHandler
注释的函数将被视为处理程序拦截器,只会在异常结果时调用。例如,为此使用带注释的函数允许您抛出更多由于抛出的数据库/服务异常而导致的域特定异常。”
但我无法让它发挥作用。我尝试添加异常处理程序如下:
@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
,并尝试捕获其他类型的异常,包括 Exception
、RuntimeException
、{{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
中,这意味着您必须处理它。这样做,您几乎可以向此类的 detail
s 字段添加任何内容,例如添加您有 ConstraintViolationException
的指示(或 ERROR_CODE,如 HTTP 协议),并且您可以在另一边打开它。
您需要的“问题”可能是知道 @ExceptionHandler
注释的方法应该驻留在处理消息的对象中。因此,如果您想对失败的命令处理操作做出反应(在您的示例中就是这种情况),您需要将异常处理程序放置在命令处理程序旁边的聚合中。
你得到一个 AxonServerRemoteCommandHandlingException
的事实表明异常是在命令调度端捕获的。因此,在 CommandGateway
/CommandBus
上调度命令之前。
然而,这是否是手头的问题,我现在不清楚,因为示例只显示异常处理程序而不是它所在的位置。请分享我对异常处理程序位置的假设是否正确。如果没有,我们将深入研究以找出原因。