在哪里关闭 java PreparedStatements 和 ResultSet?

2022-09-01 00:47:09

考虑以下代码:

PreparedStatement ps = null;
ResultSet rs = null;
try {
  ps = conn.createStatement(myQueryString);
  rs = ps.executeQuery();
  // process the results...
} catch (java.sql.SQLException e) {
  log.error("an error!", e);
  throw new MyAppException("I'm sorry. Your query did not work.");
} finally {
  ps.close();
  rs.close();
}

以上不编译,因为两者都抛出一个.那么我是否在 finally 子句中添加 try/catch 块?或者将结束语句移到 try 子句中?或者只是懒得叫近距离?PreparedStatement.close()ResultSet.close()java.sql.SQLException


答案 1

在 Java 7 中,不应显式关闭它们,而应使用自动资源管理来确保关闭资源并正确处理异常。异常处理的工作方式如下:

Exception in try | Exception in close | Result
-----------------+--------------------+----------------------------------------
      No         |        No          | Continue normally
      No         |        Yes         | Throw the close() exception
      Yes        |        No          | Throw the exception from try block
      Yes        |        Yes         | Add close() exception to main exception
                 |                    |  as "suppressed", throw main exception

希望这是有道理的。In 允许漂亮的代码,如下所示:

private void doEverythingInOneSillyMethod(String key)
  throws MyAppException
{
  try (Connection db = ds.getConnection()) {
    db.setReadOnly(true);
    ...
    try (PreparedStatement ps = db.prepareStatement(...)) {
      ps.setString(1, key);
      ...
      try (ResultSet rs = ps.executeQuery()) {
        ...
      }
    }
  } catch (SQLException ex) {
    throw new MyAppException("Query failed.", ex);
  }
}

在 Java 7 之前,最好使用嵌套的最终块,而不是测试引用是否存在 null。

我将展示的示例在深度嵌套中可能看起来很丑陋,但在实践中,精心设计的代码可能不会以相同的方法创建连接,语句和结果;通常,每个级别的嵌套都涉及将资源传递给另一个方法,该方法将其用作另一个资源的工厂。使用此方法,来自 的异常将屏蔽来自块内部的异常。这是可以克服的,但它会导致更混乱的代码,并且需要一个自定义异常类来提供Java 7中存在的“抑制”异常链。close()try

Connection db = ds.getConnection();
try {
  PreparedStatement ps = ...;
  try {
    ResultSet rs = ...
    try {
      ...
    }
    finally {
      rs.close();
    }
  } 
  finally {
    ps.close();
  }
} 
finally {
  db.close();
}

答案 2

如果你真的在手工滚动自己的jdbc,它肯定会变得混乱。最后的 close() 需要用它自己的 try catch 来包装,这至少是丑陋的。您不能跳过关闭,尽管资源将在连接关闭时被清除(如果您使用的是池,则可能不会立即清除)。实际上,使用框架(例如休眠)来管理数据库访问的主要卖点之一是管理连接和结果集处理,这样您就不会忘记关闭。

你可以做一些像这样简单的事情,这至少可以隐藏混乱,并保证你不会忘记一些事情。

public static void close(ResultSet rs, Statement ps, Connection conn)
{
    if (rs!=null)
    {
        try
        {
            rs.close();

        }
        catch(SQLException e)
        {
            logger.error("The result set cannot be closed.", e);
        }
    }
    if (ps != null)
    {
        try
        {
            ps.close();
        } catch (SQLException e)
        {
            logger.error("The statement cannot be closed.", e);
        }
    }
    if (conn != null)
    {
        try
        {
            conn.close();
        } catch (SQLException e)
        {
            logger.error("The data source connection cannot be closed.", e);
        }
    }

}

然后

finally {
    close(rs, ps, null); 
}

推荐