为什么SQLException是一个已检查异常?

21

有没有人能想到一个合理的理由,解释为什么SQLException是一个已检查的异常?

是的,可能会出现查询语法错误
是的,连接可能断开了
是的,可能存在权限问题
等等,等等,诸如此类的问题

但实际上,在生产环境下,近乎100%的时间(一旦您开始运行),都不会出现任何问题。

如果出现问题,调用代码无法执行任何恢复操作,因此应将其设置为未检查的

由于SQL的深奥性质,异常的多种原因和复杂性意味着您基本上无法进行恢复,除非异常是由于暂时的网络问题引起的,但即使在同步调用中,您也将失败,因为您不能无限期地等待网络问题得到解决,所以您必须失败事务。

通常,调用SQL看起来像这样:

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

这样的代码没有任何价值。你无法采取任何合理的措施来恢复。与其如此,不如让运行时异常冒泡——即SQLException应该是一个运行时(未检查的)异常。


4
A) 网络、数据库服务器、负载均衡器等并没有100%的运行时间。 B) 当出现故障时,你肯定可以采取一些措施。 - Brian Roach
1
@BrianRoach 好的 - 假设网络不可用了。调用代码能做什么来恢复网络呢?什么也做不了!那么,为什么还要检查呢?检查意味着调用者可以恢复。 - Bohemian
1
@BrianRoach 数据库连接池中的连接在分配给您使用之前会被检查。极不可能出现连接问题。所有良好应用程序中都有一个顶级的 catch Exception,它会捕获未经检查的异常,就像它捕获NPE一样;只是因为异常未经检查并不意味着您的“应用程序将崩溃”。如果您想要的话,仍然可以捕获特定的未经检查的异常。 - Bohemian
如果你通常想在SQLException上返回,则应该高兴它是一个已检查的异常,因为它与默认的RuntimeException行为不同:让RuntimeException冒泡。 - blacelle
1
@bla 但是SQLException实际上就是一个Error:你基本上无法从中恢复 - 你的应用程序已经崩溃了 - 而且Errors是不受检查的。为什么SQLException也不是呢。 - Bohemian
显示剩余9条评论
4个回答

3

实际上,几乎100%的时间都没有问题 - 这仅限于你自己的观察,并不代表其他系统。世界各地有各种计算机系统,存在各种瓶颈。您的成功率几乎达到了100%。其他人必须处理低得多的百分比。

常见误解是根据其出现的频率来考虑引入/删除Checked Exception。Checked Exceptions作为通信渠道。正如您所知,每个方法都有其公共接口。这样,方法告诉我们它接受哪些参数以及其主体代码的结果。

当正在进行的方法无法实现其承诺(例如返回值)时,它需要一种方法告诉其他方法出了问题,它无法完成预期的任务。但是该怎么做?将消息作为返回的值发送不起作用,调用方法几乎无法区分正确值和错误消息。更不要说一些方法将void作为返回值。那么当您无法遵守由您的方法接口定义的承诺时该怎么办?好吧,您抛出异常(发送消息)。

如果您期望ResultSet并且未建立与数据库的连接,应该怎么办?返回空ResultSet吗?当然不,这告诉我们数据库是空的。返回null?这只会委托问题并使查找原因变得不清楚。

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

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


4
如果系统在很大一部分时间内失效,那么人们就不会使用它们。成功率接近100%,可以说是100%。我并不是说不要抛出异常,只是应该将其设置为未经检查的异常。让其向上冒泡。正如我所说,一个经过检查的异常意味着调用者可以采取措施进行恢复,但是对于 SQL 来说,你无法恢复。如果你收到语法错误异常,那么在你的代码中你能做些什么呢?什么都不能做!你可能会记录错误并退出方法 - 你没有添加任何价值。它与未捕获异常的情况几乎没有区别。 - Bohemian
JDBC 库并不负责强制要求人们处理基础设施可能会崩溃的事实。如果无法访问数据库,则使 SQL 操作失败并说明原因。在 SQLException 发生的情况下,您将像处理运行时异常一样处理它,因为这确实就是它的本质。 - Usman Mutawakil
我还想补充一点,OP的问题与消除SQLException无关,而且在JDBC底层工作方式中,如果会话/连接失败,你无法返回ResultSets和类似的对象。我认为这个答案比问题所关心的层次低了好几层。 - Usman Mutawakil

2
有几种方法可以解决已检查与未检查的问题。检查调用代码是否可以从异常中恢复是一种方法,但我同意这种方法并不能解释为什么SQLException是一个已检查的异常。
马丁·福勒在他的著作《重构》中提供了另一种方法,建议验证是调用者还是被调用者的责任来进行可能导致异常的检查。
如果调用者方法在调用被调用者方法之前应该执行检查(例如确保参数不为空),那么如果没有进行此检查,那么显然是编程错误,然后被调用方法应该抛出一个未检查的异常。
现在,如果是被调用者方法负责进行检查,因为只有这个方法知道如何进行这样的检查,那么从被调用者方法抛出的异常应该是已检查的
SQLException而言,我认为只有这个类才知道:
  • 查询中存在语法错误,这取决于数据库
  • 连接已断开
  • 存在权限问题

好的。假设我们要检查SQL语法是否正确(通过在Java中重新编写SQL解析器!),并且出现错误-你会怎么做?什么都不做!你无法修复它,那么为什么要捕获异常?你唯一要做的就是记录异常并抛出另一个异常-为什么不让未经检查的异常上升呢? - Bohemian
2
好的,我同意,在大多数情况下,您的应用程序将无法在这种情况下恢复。但是,在“连接已断开”的情况下,它可以通过重新建立与数据库的连接轻松恢复。我想,您认为异常是否可恢复取决于调用方法的作者,这意味着它不能用作决定检查异常与未检查异常之间的标准。 - Adam Siemion

2
一个原因是方法应该抛出与其所做的抽象级别一致的异常。因此,从数据库加载信息的方法不应该引发 SQLException,而应该引发 ResourceNotFoundException 或 ResourceUnavailableException。将 SQLException 设为 checked 异常是强制开发人员捕获异常并将其包装在这个新的抽象级别中的一种方式。这个观点来自 Joshua Bloch 的《Effective Java 第二版》(第61条:抛出适合抽象级别的异常)。

1
但是,“从数据库加载数据”为什么会失败呢?事实上,它并不会失败!它可能会“失败”,因为找不到一行数据,但这不会抛出SQLException。如果确实失败了,它会“爆炸”,调用者无法进行任何恢复操作 - 这就是使用未经检查的异常的本质原因。 - Bohemian
在大多数情况下,除了“爆炸”应用程序外,没有什么可做的。但我可以想象一些情况下不应该这样做。例如,如果存在备份数据库,或者您无法获取的信息不在主数据库中(例如,在电子商务网站上获取愿望清单时出现错误)。 - obourgain
请注意,“信息不是主数据库”不会抛出异常 - 您只会得到一个空行集。您正在谈论主数据库已经崩溃 - 这非常灾难性。如果您有处理此问题的代码,则可以捕获未经检查的SQLException并执行某些操作。如果人们没有这种能力怎么办 - 他们希望SQLException成为运行时异常。 - Bohemian
ResourceNotFoundException 是一个公平的异常,但我认为如果你正在构建这种围绕 JDBC 的库或 SDK,你会想要在任何可能出现错误的情况下将许多低级方法包装在通用的 Try/Catch/Exception 块中,但你仍然希望底层的 SQLException 及其原因被知晓。这并不像你会将 SQLException 改成 ResourceNotFound 而不提到 SQL 语法错误、连接池关闭等等。 - Usman Mutawakil

1
捕获异常使我们能够从异常情况中恢复,这是真的,但它们还允许我们做其他事情。
在运行时无法从SQLException中恢复,因为你没有什么可以修复问题的方法,但有一些有用的事情可以做:
- 记录异常以获取调试信息 - 回滚事务
你可以总是在更高层次(或更低层次,具体取决于角度)记录异常,但你会失去一些语义价值,无论是在调试时还是在审查代码时。
如果你像这样做:
try { ... }
catch(SQLException e) 
{ 
    SomeLogger.log(...);
    rollback();
    throw e;
}

如果你稍后回到这段代码,你会立即意识到try中的代码可能会失败,而不必在脑海中解析代码以确定是否会失败。
另一件事是确保释放了任何数据库资源,尽管我不确定这是否可以轻松实现。

2
问题不在于捕获异常的相关性(因为RuntimeException可以被捕获),而在于强制捕获它的相关性(在方法签名中添加throw SQLException可能也不相关,根据问题,在大多数情况下除了重新抛出之外什么也不做)。 - blacelle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接