【问题标题】:Should I catch exceptions thrown when closing java.sql.Connection我应该捕获关闭 java.sql.Connection 时抛出的异常吗
【发布时间】:2008-10-29 19:43:28
【问题描述】:

Connection.close() 可能会抛出SqlException,但我一直认为忽略任何此类异常是安全的(而且我从未见过不忽略它们的代码)。

通常我会写:

 try{
    connection.close();
 }catch(Exception e) {}

或者

 try{
    connection.close();
 }catch(Exception e) {
     logger.log(e.getMessage(), e); 
 }

问题是:

  1. 这是一种不好的做法(并且是否有人在忽略此类异常时遇到问题)。
  2. Connection.close() 确实抛出任何异常时。
  3. 如果不好,我应该如何处理异常。

评论:

我知道丢弃异常是邪恶的,但我只提到关闭连接时抛出的异常(正如我所见,这在这种情况下相当普遍)。

有人知道Connection.close() 什么时候可以扔东西吗?

【问题讨论】:

  • 我总是忽略它们,看看我现在通常如何处理数据库。
  • 空的 catch 块是邪恶的。就像值得开枪一样。我所说的开火,是指在火刑柱上燃烧。即使是简单的日志语句也足够了,伙计们!
  • 当凌晨 3 点出现生产问题时,那些有空的 catch 语句“志愿者”会自动排在首位。

标签: java jdbc coding-style


【解决方案1】:

实际上,您正在做的(几乎)是最佳实践 :-) 这是我在 Spring 的 JdbcUtils.java 中看到的。所以,你可能想添加 另一个 Catch 块。

/**
 * Close the given  ResultSet and ignore any thrown exception.
 * This is useful for typical finally blocks in manual  code.
 * @param resultSet the  ResultSet to close
 * @see javax.resource.cci.ResultSet#close()
 */
private void closeResultSet(ResultSet resultSet) {
  if (resultSet != null) {
    try {
      resultSet.close();
    }
    catch (SQLException ex) {
      logger.debug("Could not close  ResultSet", ex);
    }
    catch (Throwable ex) {
      // We don't trust the  driver: It might throw RuntimeException or Error.
      logger.debug("Unexpected exception on closing  ResultSet", ex);
    }
  }
}

【讨论】:

  • 处理Throwable需要什么?您能告诉我们关闭连接时可能发生的错误吗?
  • 嗨@NikeshDevaki:这些错误可能来自 jdbc 驱动程序。取决于供应商、数据库和版本。现在不记得了——我发布原始答案已经 10 年了:-(
  • 哇! 10年?很高兴听你这样说。似乎没有什么改变开发者的思维方式和遇到的问题。
【解决方案2】:

一般来说,我已经被人们扔掉这样的异常所浪费了。

我建议遵循一些基本规则,但有例外:

如果您绝对确定您永远不会对已检查异常造成问题,请仅捕获该异常并准确说明您不需要处理它的原因。 (Sleep 会抛出一个 InterruptedException,除非你真的对它感兴趣,否则它总是可以被忽略,但老实说,这是我通常忽略的唯一情况——即使那样,如果你从来没有得到它,那么记录它的成本是多少?)

如果您不确定,但您可能偶尔会得到它,请捕获并记录堆栈跟踪,以便在它导致问题时可以找到它。同样,只捕获您需要的异常。

如果您看不到任何可以抛出已检查异常的方式,请捕获它并将其作为未检查异常重新抛出。

如果您确切知道导致异常的原因,捕获它并准确记录原因,如果您非常清楚导致异常的原因,则在这种情况下您实际上不需要堆栈跟踪(并且您可能会提到类如果您还没有使用 log4j 或其他东西,请记录它。

听起来你的问题属于最后一类,对于这种捕获,永远不要做你写的(异常 e),总是做特定的异常,以防抛出一些未经检查的异常(错误的参数,空指针,...)

更新:这里的主要问题是 Checked Exceptions 不好。它们存在的唯一高度使用的语言是Java。它们在理论上很简洁,但实际上它们会导致这种捕获和隐藏的行为,而未经检查的异常是不会出现的。

很多人评论说,我说有时隐藏它们是可以的。具体来说,我能想到的一种情况是:

try {
    Thread.sleep(1000);
catch (InterruptedException e) {
    // I really don't care if this sleep is interrupted!
}

我想我觉得这种用法没问题的主要原因是因为这种 InterruptedException 的使用首先是对检查异常模式的滥用,它传达的睡眠结果不仅仅是指示异常条件。

这样会更有意义:

boolean interrupted=Thread.sleep(1000);

但是当他们第一次创建 Java 时,他们为他们新的检查异常模式感到非常自豪(可以理解,它在概念上真的很简洁——只是在实践中失败了)

我无法想象另一种可以接受的情况,所以也许我应该将其列为 单一情况,在这种情况下忽略异常可能是有效的。

【讨论】:

  • 忽略异常从不是个好主意,即使您评论了原因。记录异常的开销最小,这是应该做的最低限度,因为当你“......绝对肯定你永远不会引起问题......”时,问题会在以后发生,而不是现在。
  • 我最大的例子是 sleep() 它会抛出一个检查异常,通常永远无法达到,如果达到,谁在乎?如果我在乎(当然),我会处理的。但是,请发表评论。
  • 我曾经在一个充斥着抑制尝试捕获的系统上工作,大多数人都评论说“这不应该发生”。他们是对的,错误永远不应该发生,但他们确实发生了。我学到的教训,永远不要压制异常。它们不可避免地会发生。
  • Jeremy:我澄清了我的帖子重新“吃”了一个例外。如果您认为我们没有达成一致,那么您对这篇文章的理解有点小,因为我建议任何“不应该发生”的事情都应该被捕获并作为未经检查的异常重新抛出,因为您是完全正确的。
  • 嗯.... sleep 抛出一个 InterruptedException 是有原因的。如果您实际上正在编写一个多线程程序,并且其他一些线程在当前处于睡眠块的线程上调用 Thread.interrupt() ,那么这就是引发异常的地方。这是程序员的一个指标,他们应该停止执行他们正在做的事情并允许后台进程(无论线程正在做什么)优雅地终止。
【解决方案3】:

至少,always always always 记录您正在捕获但未采取行动的异常。

无声无息地捕获的异常是最糟糕的。

【讨论】:

  • 我相信您必须调试此类代码,然后才能看到这样做的价值。
【解决方案4】:

我个人喜欢您至少记录错误的第二个想法。因为您正在捕获异常,所以理论上可以捕获除 SQL 异常之外的其他内容。我不确定会发生什么或有多罕见(如内存不足异常等),但抑制所有错误对我来说似乎不合适。

如果您想抑制错误,我只会对您知道应该以这种方式处理的非常具体的错误进行处理。

假设情况:如果您的 sql 有一个打开的事务,并且因此关闭连接导致异常,您想抑制该错误怎么办?即使抑制 SQLExceptions 也可能有点危险。

【讨论】:

    【解决方案5】:

    您必须处理异常。这不是一个坏习惯。想象一下,您在关闭数据库连接之前丢失了网络。它可能会抛出异常。

    很少见吗?是的。我想这就是他们所谓的异常,这不是忽略它的理由。请记住,如果它可能失败,它就会失败。

    您还应该考虑此时是否可能有空连接(这会导致 NullPointerException)。

    if (connection != null) {
       try { 
          connection.close(); 
       } catch (SQLException sqle) { 
          logger.log(e.getMessage(), e); 
       }
    }
    

    【讨论】:

      【解决方案6】:

      在理想世界中,你永远不应该对异常不做任何事情,当然,在理想世界中,你也永远不会得到异常 8-)

      因此,您必须检查各种选项的影响。

      仅记录:数据库操作已全部完成,除了清理资源外无事可做。如果此时发生异常,它很可能对执行的工作没有影响,因此记录错误就足够了。当然,如果在记录过程中发生错误,那么您基本上必须处理实际上并没有失败的失败数据库操作。

      Empty handler:数据库操作全部完成,除了清理资源外,别无他法。如果此时出现异常,很可能对执行的工作没有影响,所以方法成功返回。下一次数据库访问可能会遇到同样的问题,但它应该发生在事务开始时,它会正确地失败,然后得到适当的处理。如果问题已自行解决,则不会有任何迹象表明出现任何问题。

      将 close() 操作放在 finally 块中以确保进行清理是非常典型的场景,因为我们不希望任何其他故障阻止资源清理。如果没有发生错误,那么当它的操作成功完成时,你的方法不应该失败。在这种情况下,空异常处理是很正常的。

      当然意见会有所不同。

      【讨论】:

        【解决方案7】:

        你也可以抛出一个 RuntimeException:

        try {
            connection.close();
         } catch(Exception e) {
             throw new RuntimeException(e); 
         }
        

        您不必更改方法签名,以后可以使用 Exception.getCause 方法查找问题的原因。

        【讨论】:

        • 这是一个非常好的解决很多检查异常的方法。我不确定你为什么被否决,我为你修复了它:) 实际上你可能想要创建一个更具体的运行时异常来重新抛出它。
        • 将其作为运行时异常抛出的原因是什么?
        • 总是包含 reason 为什么要包装和重新抛出作为消息。请记住,堆栈跟踪中的信息越多,对试图弄清楚发生了什么的人就越有帮助。特别是如果那个人不熟悉代码。
        • 我喜欢这个,比简单地记录异常更好,重新抛出异常可能有助于阻止进一步的损害,但实际上取决于用例,通常在简单的人工任务处理中(人工使用 UI 活动),close() 不太可能抛出异常,这种情况更有可能发生在批处理中,当处理大量数据等时,或者多线程的错误实现可能
        【解决方案8】:

        请注意,Apache Commons DButils 提供了一个 closeQuietly() 方法,您可以使用该方法来避免“冗余”捕获使代码混乱。请注意,我并不是在提倡吞咽异常,但对于这种 close() 场景,我认为它通常是可以接受的。

        【讨论】:

        • 正确的关闭错误处理实际上很难做到。
        【解决方案9】:

        根据我的经验,忽略异常绝不是一个好主意。 相信我,如果您记录异常,生产支持工程师和分析师会非常感谢您。

        此外,如果您使用正确的日志记录框架,则异常对性能的影响为零或最小。

        【讨论】:

          【解决方案10】:

          如果这是一个“永远不会发生的错误”的情况,那么我将重新抛出一个异常,并希望没有人捕捉到它。
          如果这是任何其他情况,我可能会记录它

          【讨论】:

            【解决方案11】:

            如果你能处理它,那么就这样做(如果它是意外的,请记录它)。如果你不能处理它,然后正确地重新抛出它,以便上面的一些代码可以处理它。

            默默地吞下异常是为修复代码的人遗漏了关键信息。

            【讨论】:

              【解决方案12】:

              在关闭与数据库的连接时处理异常是一种更好的做法。因为,稍后在您的代码中的某个时间点,如果您尝试访问语句或结果集对象,那么它将自动引发异常。所以,最好处理异常。

              【讨论】:

                猜你喜欢
                • 2013-05-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-02-17
                • 2014-01-16
                • 2014-07-09
                • 1970-01-01
                • 2014-05-02
                相关资源
                最近更新 更多