【问题标题】:Why is SQLException a checked exception [closed]为什么 SQLException 是一个检查异常[关闭]
【发布时间】:2013-05-08 18:13:13
【问题描述】:

谁能想到SQLException 是一个已检查异常的合理原因?

是的,查询中可能存在语法错误
是的,连接可能已断开
是的,可能存在权限问题
等等等等等等等等等等

但几乎 100% 的时间(一旦您在生产环境中运行),没有任何问题。

如果出现问题,调用代码无法进行任何恢复,因此应该取消选中

检查会在整个代码中创建大量敷衍的try catch 块,任何参与过使用 JDBC 的项目的人都会证明这一点。代码混乱很严重。

由于 SQL 的深奥性,你可能会得到一个 SQLException 的无数原因及其复杂性意味着你基本上无法恢复,除非异常是由临时网络问题引起的,但即使在同步调用中,你'无论如何都会沉没,因为您不能无限期地等待网络问题得到解决,因此您将不得不使交易失败。

通常,调用 SQL 如下所示:

try {
    // make some SQL call(s)
} catch {SQLException e) { 
    // log the exception
    return; // and give up
}

这样的代码没有任何价值。您无法采取任何合理的措施来恢复。您不妨让运行时异常冒泡——即 SQLException 应该是 runtime(未经检查的)异常。

【问题讨论】:

  • A) 网络、数据库服务器、负载平衡器等没有 100% 的正常运行时间 B) 当发生故障时,您当然可以做点什么。
  • 服务器故障发生的频率比您预期的要高,这是客户端应该处理的事情。 (很可能是通过显示错误消息,稍后再试)。主要是死锁问题。
  • @BrianRoach 好的 - 说网络消失了。调用代码可以做什么来恢复网络?没有!那么,再次,为什么要检查它? Checked 表示调用者可以恢复。
  • @BrianRoach 来自连接池的数据库连接在提供给您使用之前会被检查。您不太可能遇到连接问题。在所有优秀的应用程序中总会有一个顶级的catch Exception 来捕获未经检查的异常,就像它捕获 NPE 一样;仅仅因为未检查异常并不意味着您的“应用程序将崩溃”。如果您愿意,仍然欢迎您捕获特定的未经检查的异常。
  • @bla 但是SQLException 实际上是Error:您基本上无法从它们中恢复-您的应用程序已被冲洗-并且Errors 未选中。为什么不也SQLException

标签: java exception coding-style checked unchecked


【解决方案1】:

几乎 100% 的时间没有任何问题 - 这仅限于您自己的观察,对其他系统一无所知。世界各地存在各种具有各种瓶颈的计算机系统。您的成功率几乎是 100%。其他人必须处理低得多的百分比。

常见的误解是考虑通过发生频率引入/删除检查异常。已检查的异常用作通信渠道。如您所知,每个方法都有其公共接口。这样,方法告诉我们它接受哪些参数以及它的主体中的代码的结果是什么。

当当前正在执行的方法无法兑现其承诺(例如返回值)时,它需要一种方法来告诉另一个方法出现问题并且无法执行预期的操作。但是怎么做呢?将消息作为返回值发送是行不通的,调用方法几乎没有机会区分正确的值和错误消息。并不是说某些方法具有 void 作为返回值。那么,当您无法遵守方法接口定义的承诺时,您会怎么做?好吧,你抛出一个异常(发送消息)。

如果您期望 ResultSet 并且没有建立与您的数据库的连接,您应该怎么做?返回空结果集?不,这告诉我们数据库是空的。返回空?好吧,这只会让问题变得更简单,让查找原因变得不清楚。

您可以使用该空结果集并将其作为对另一个数据库的另一个查询的一部分,从而使其不一致。

没有 SQLException,即使一个错误也可能导致数据不一致。

【讨论】:

  • 如果系统在相当大比例的时间内出现故障,人们就不会使用它们。成功是如此接近 100%,你不妨称之为 100%。我并不是说 not 抛出异常,只是应该取消选中它。让它冒泡。正如我所说,检查过的异常意味着调用者可以做一些事情来恢复,但使用 SQL 你无法恢复。如果您遇到语法错误异常,您将在代码中执行什么操作?没什么,就是这样!您可能会记录错误并退出该方法 - 您没有添加任何价值。它还不如没有抓住它。
  • JDBC 库没有责任强迫人们处理他们的基础设施可能出现故障的事实。如果无法访问数据库,则让 SQL 操作爆炸并说出原因。在发生 SQLException 的情况下,您将与发生运行时异常一样处理它,因为它确实是这样。
  • 我还想补充一点,OP 的问题与消除 SQLException 以及 JDBC 在后台工作的方式无关,如果会话/连接失败,您将无法返回 ResultSets 和类似对象。我认为这个答案比问题所关注的层低了几层。
【解决方案2】:

有几种方法可以解决已检查与未检查的困境。检查调用代码是否可以从异常中恢复是一种方法,但是我同意,这种方法并不能解释为什么SQLExcption 是一个已检查 异常。

Martin Fowler 在他的巨著“重构”中提供的另一个建议是验证是 calling 还是 called 方法的责任来进行检查,这可能导致异常。

如果 调用者 方法应该在调用 被调用 方法之前执行检查(例如确保参数不为空),那么如果尚未完成此检查显然是一个编程错误,然后调用方法应该抛出一个unchecked异常。

现在如果是被调用方法负责进行检查,因为只有这个方法可以知道如何执行这种检查,那么被调用方法抛出的异常应该被检查

如果是SQLException,我想只有这个类可以知道:

  • 查询中存在语法错误,因为这取决于数据库
  • 连接已断开
  • 存在权限问题

【讨论】:

  • 好的。让我们假设检查 SQL 语法是否正确(通过在 java 中重写 SQL 解析器!),并说有一个错误 - 你打算怎么办?没有!您无法修复它,那么为什么要捕获异常?您要做的唯一操作是记录异常并引发另一个异常 - 为什么不让未经检查的异常渗透?
  • 好的,我同意,在大多数情况下,您的应用程序在这种情况下将无法恢复。但是在“连接已死”的情况下,它可以通过重新建立与数据库的连接轻松地从中恢复。我猜,您感知异常是否可恢复的方式仅取决于调用方法的作者,这意味着它不能用作决定检查异常还是未检查异常的标准。
【解决方案3】:

一个原因是一个方法应该抛出与该方法所做的抽象级别一致的异常。

因此,从数据库加载信息的方法不应引发SQLException,而应引发ResourceNotFoundExceptionResourceUnavailableException

检查SQLException 是一种强制开发人员捕获异常并将其包装在这个新抽象级别中的方法。

这个论点取自 Joshua Bloch 的 Effective Java Second Edition(第 61 条:抛出适合抽象的异常)。

【讨论】:

  • 但是为什么“从数据库加载数据”会失败呢?事实是,它不会失败!如果没有找到行,它可能会“失败”,但这不会引发 SQLException。如果它确实失败了,它会“爆炸”并且调用者无法做任何事情来恢复——这就是为什么要使用未经检查的异常的本质。
  • 我同意在大多数情况下除了“分解”应用程序之外别无他法。但我可以想象一些你不应该的情况。例如,如果存在备份数据库,或者您无法获取的信息不在主数据库中(例如,在电子商务网站上获取您的愿望清单时出错)。
  • 请注意,“信息不是主数据库”不会引发异常 - 您只会得到一个空行集。你说的是主数据库已经关闭 - 非常灾难性。我有代码来处理这个问题,没有什么能阻止你捕捉未经检查的 SQLException 并做一些事情。如果人们没有这种能力怎么办——他们希望 SQLException 成为一个运行时。
  • ResourceNotFoundException 是一个公平的异常,但我认为如果您围绕 JDBC 构建这种类型的库或 SDK,您无论如何都希望在通用 Try/Catch/Exception 块中包装许多较低级别的方法, 对于任何可能的错误,但您仍然希望了解底层 SQLExeption 及其原因。它不像您将 SQLException 更改为 ResourceNotFound 而不提及 SQL 语法错误、连接池关闭等。
【解决方案4】:

捕获异常使我们能够从异常情况中恢复,这是真的,但它们也允许我们做其他事情。

您无法从 SQLException 中恢复,因为您无法在运行时解决问题,但您可以做一些有用的事情:

  • 记录异常以获取调试信息
  • 回滚交易

您始终可以在更高级别(或更低级别,取决于视角)记录异常,但在调试时和审查代码时都会丢失一些语义价值。

如果你这样做:

try { ... }
catch(SQLException e) 
{ 
    SomeLogger.log(...);
    rollback();
    throw e;
}

稍后再回到这段代码,你会立即意识到 try 中的代码可能会失败,而无需在心里解析代码以确定它是否会失败。

您可以做的另一件事是确保已释放所有数据库资源,但我不确定这是否会立即发生。

【讨论】:

  • 问题不是关于捕获异常的相关性(因为可以捕获 RuntimeException),而是关于强制捕获它的相关性(因为在方法签名中添加 throw SQLException 可能也不相关并且根据问题,在大多数情况下,除了重新抛出之外什么都不做)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-23
  • 2014-09-03
  • 2012-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多