【问题标题】:Exception versus return code in DAO patternDAO 模式中的异常与返回码
【发布时间】:2015-02-21 23:11:18
【问题描述】:

在阅读了很多关于 Java 中滥用异常以及如何让异常在应用程序的不同层中冒泡之后,我已经到了一个我不知道我应该做什么的地步处理我的应用程序可能存在的潜在错误。

基本上,我有一个 Web 服务,它使用 DAO 模式来访问我的数据库中的数据。所有数据库操作都可以抛出SQLException。 到今天为止,我正在使用 try catch 来捕获 SQLException,然后抛出一个名为 ExceptionDAO 的特定定义异常,该异常将由 web 服务处理,以向我的用户(移动应用程序)返回正确的消息网络服务。

在阅读了很多关于异常应该如何异常并且不应该在控制流中使用的内容之后,我对如何处理任何错误有了一个混合的理解:

  • 对任何可能发生的事情(例如用户名已经存在)使用返回码,因此,为了符合 DAO 模式,请将我的业务对象作为参数传递。我也可以使用一个特定的对来代替返回代码+业务对象。然后,Web 服务将使用返回代码来显示特定消息。
  • 对任何我无法预测会发生的事情使用已检查的异常,并让它们冒泡到 Web 服务来处理并向用户返回消息。 (例如,我无法预测的 SQLException:连接中止)
  • 在这种情况下,让未经检查的异常也冒泡并显示一种 404 错误。

我还查看了 null 模式,但我认为它不太适合这种特殊情况。 我还担心不要向用户提供太多信息,而是提供有用且直截了当的信息。实际上,网络服务返回的消息将被移动应用程序使用,然后向最终用户显示消息。

我希望我对我遇到的问题足够清楚,我期待您的回答!

【问题讨论】:

  • 这是一个重要的话题,但对于 Stack Overflow 问题来说可能过于宽泛!您还可以在programmers.stackexchange.com 上找到一些相关的答案
  • @DNA 没错,我在程序员 stackexchange 上阅读了很多关于我的问题的主题!我还应该把它留在这里吗?
  • 我建议只使用 Spring Data 而不是自己编写。
  • @chrylis 好吧,从我目前所看到的情况来看,这一切都可以解决引发异常的问题,这并不是我的问题所在。我想知道我应该一直使用异常还是混合使用异常和返回码。
  • 不要使用返回码。如果数据库代码是您自己的,您应该考虑抛出更具体的SQLException 或您自己的自定义异常。您需要确定的另一件事是谁需要减轻异常。例如,如果您尝试写入文件但该文件不存在,您可以执行以下两种操作之一:1) 创建一个空白文件并向其写入数据,或 2) 使用异常作为通知机制用户并让用户决定做什么。如果正确使用异常处理,您的代码会更简单。返回错误代码不是要走的路。

标签: java web-services exception exception-handling dao


【解决方案1】:

返回码适用于 C,它的特性中缺少异常处理。对于 Java,请使用已检查或运行时的异常,这是个人喜好问题。

就个人而言,我讨厌检查异常,因为它们会污染我的方法签名,其中包含可能永远不会发生的边界情况的信息。但也许你想严格遵守你的班级合同,即使是在这种特殊情况下。如果这是您的情况,请使用已检查的异常。否则,只要检测到异常情况(例如未找到实体、实体已存在等),就让您的方法签名放心并抛出运行时异常。

请注意ExceptionDAO 不是一个好名字。它似乎是一个处理异常的 dao。也许像PersistenceException 这样的东西会更好。

除了命名细节之外,我认为您的方法是正确的,尽管并不理想。理想情况下,您不需要在每个从 DAO 调用方法的方法(或在 DAO 的每个方法中)为 SQLExceptions 执行 try/catch。相反,持久性异常翻译机制会好得多。例如,春天,comes with one by default。 Spring 通过代理每个 DAO 并让其代理在每个方法调用周围执行try/catch 来实现这一点。然后,带有特定 SQL 错误代码的特定 SQLExceptions 被转换为 Spring 自己的 DataAccessException 层次结构。所以,在上层,你最终会得到一个特定的DataAccessException,但你不需要在每个方法中都写一个try/catch

如果您已经在使用 Spring,那么您无事可做,但如果您没有使用它并且有许多 DAO 或许多可能抛出 SQLExceptions 的方法,并且 您所有的 DAO 都实现了一个接口,那么实现一个拦截 DAO 的所有方法并围绕它们执行try/catch 的代理可能是值得的。然后,在这个拦截器的 catch 块中,您将抛出您的 ExceptionDAO(请重命名它!),并带有一条取决于原始 SQLException(也可能取决于其 SQL 错误代码)的消息。

这种方法的优点是您可以在程序的一个点中处理所有持久性异常

同样的概念也可以应用于您的 Web 层。您可以创建一个代理来拦截每个方法调用并围绕它执行try/catch,而不是让您的端点的每个方法处理您的ExceptionDAO(不要忘记重命名它!)。然后,您可以从异常中提取消息并将其发送到响应中(或任何您认为合适的方式)。同样,这一切都基于 Spring,这一次,在 Spring MVC Exception Handling 机制中。在这里,您还可以处理意外异常,即RuntimeExceptions 并向您的用户提供适当的消息。

同样的优势在于,您可以在程序的一个点中处理所有到达 Web 层的异常。

请参阅 Proxy javadocsthis tutorial 以获取有关 Java 代理的更多参考。

【讨论】:

  • 感谢您提供如此详尽的答案。是的,命名不是很好,我会改变它。即使我可以创建您正在谈论的所有机制,但我认为使用 Spring 会更好,对吧?当我将 Jersey 用于我的 web 服务时,我会使用 Spring(或 SpringMVC)吗?此外,我在我的 DAO 类中使用资源尝试来保证资源的状态,我想 Spring 已经执行了这个?
  • 是的。使用 Spring 更好。它使用 @Repository@ExceptionHandler 注释来执行此操作。
  • 我想你答案中的“这个”是指尝试资源?我也看到很多只使用 Spring 的例子,那我需要 SpringMVC 吗?
  • 对不起。我的意思是你最好使用Spring。是的,Spring 可以正确处理数据库连接和资源。你不必自己做。
  • 没错。弹簧是模块化的。您只需添加您需要的模块。在您的情况下,它将是 spring mvc 和 spring core。我相信您还需要一些依赖项,也许是 spring 上下文。
【解决方案2】:

我认为您缺少一个重要的选项,即观察者模式。

在你的 DAO 中,你可以拥有这个:

public interface OnExceptionInteractionListener {
    public void onExceptionInteraction(String exceptionMessage);
}

我希望 DAO 类似于:

public SomeDAO(OnExceptionInteractionListener listener, ...) {
}

我会将 DAO 实例化为

SomeDAO s = new SomeDAO(new OnExceptionInteractionListener() {
  public void onExceptionInteraction(String exceptionMessage) {
  }
}, ...);

因此,如果捕获到异常,则调用 DAO 中的侦听器,下一级将处理它。

这比抛出异常的性能要好,比返回码要好。

有关更多信息,您可以查看此答案,其他示例:https://stackoverflow.com/a/18585099/67566

我还没有尝试过,但如果您使用的是 Java8,我希望 lambda expressions 对此也有用。

【讨论】:

  • 我没有考虑观察者模式,但这似乎是一个优雅的解决问题的方法,没有太多的样板。是的,我们使用的是 Java 8,而 lambda 表达式可能会对此有所帮助!
【解决方案3】:

返回代码非常老派。理想情况下,您应该在某种检查事务中进行插入:

  1. 锁定用户名插入

  2. 这个用户名可用吗?

    3a。如果是,插入并解锁

    3b。如果没有,解锁并通知用户

如果这是不可能的,那么如果结果创建成功,则返回一个包装结果的对象,并以潜在的结果响应 Enum。

【讨论】:

  • 你是说当我需要执行插入时我应该锁定数据库上的每个插入?在我的情况下这是不可能的,我正在使用连接池,可能有很多用户使用 Web 服务,因此也使用数据库。
  • 这专门针对您提到的用户名已经存在的情况。如果用户名是唯一的,甚至不应该尝试这种插入。我的解决方案非常粗粒度且易于执行,但是如果您在该表上有大量吞吐量,您可以想出一些更好的东西。就像排队用户创建请求一样,有一个“暂存”,其中单个用户名被锁定,直到该过程完成。不管怎样,我只是说这应该是一个明确的过程,而不是一个例外。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-07
  • 2018-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多