43

考虑代码:

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();
}

上面没有编译,因为两者都PreparedStatement.close()抛出ResultSet.close()java.sql.SQLException. 那么我应该在 finally 子句中添加一个 try/catch 块吗?或者将 close 语句移到 try 子句中?或者只是不打扰打电话关闭?

4

13 回答 13

54

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

尝试中的异常 | 关闭异常 | 结果
-----------------+--------------------+------------ -----------------------------------------
      没有 | 没有 | 继续正常
      没有 | 是 | 抛出 close() 异常
      是 | 没有 | 从 try 块中抛出异常
      是 | 是 | 将 close() 异常添加到主异常
                 | | 作为“抑制”,抛出主要异常

希望这是有道理的。在允许漂亮的代码,像这样:

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 之前,最好使用嵌套的 finally 块,而不是测试 null 的引用。

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

Connection db = ds.getConnection();
try {
  PreparedStatement ps = ...;
  try {
    ResultSet rs = ...
    try {
      ...
    }
    finally {
      rs.close();
    }
  } 
  finally {
    ps.close();
  }
} 
finally {
  db.close();
}
于 2008-11-26T19:29:47.680 回答
29

如果您真的在手动滚动自己的 jdbc,它肯定会变得一团糟。finally 中的 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); 
}
于 2008-11-26T17:10:40.300 回答
12

对于文件 I/O,我通常在 finally 块中添加一个 try/catch。但是,您必须小心不要从 finally 块中抛出任何异常,因为它们会导致原始异常(如果有)丢失。

有关关闭数据库连接的更具体示例,请参阅本文

于 2008-11-26T17:09:30.350 回答
7

不要浪费时间编写低级异常管理,使用像 Spring-JDBC 这样的高级 API 或围绕连接/语句/rs 对象的自定义包装器来隐藏杂乱的 try-catch 代码。

于 2008-11-26T17:20:03.137 回答
7

另请注意:

“当一个 Statement 对象关闭时,它当前的 ResultSet 对象(如果存在)也将关闭。”

http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close()

仅在 finally 中关闭 PreparedStatement 就足够了,并且仅当它尚未关闭时。但是,如果您想真正特别,请先关闭 ResultSet,而不是在关闭 PreparedStatement 之后(之后关闭它,就像这里的一些示例一样,实际上应该保证异常,因为它已经关闭了)。

于 2009-03-25T15:13:20.490 回答
5

我通常有一个实用方法可以关闭这样的事情,包括注意不要尝试使用空引用做任何事情。

通常如果close()抛出我并不关心的异常,所以我只是记录异常并吞下它 - 但另一种选择是将它转换为RuntimeException. 无论哪种方式,我都建议使用易于调用的实用方法来执行此操作,因为您可能需要在许多地方执行此操作。

请注意,如果关闭 PreparedStatement 失败,您当前的解决方案不会关闭 ResultSet - 最好使用嵌套的 finally 块。

于 2008-11-26T17:11:23.543 回答
4

以@erickson 的回答为基础,为什么不像这样在一个try街区内完成呢?

private void doEverythingInOneSillyMethod(String key) throws MyAppException
{
  try (Connection db = ds.getConnection();
       PreparedStatement ps = db.prepareStatement(...)) {

    db.setReadOnly(true);
    ps.setString(1, key);
    ResultSet rs = ps.executeQuery()
    ...
  } catch (SQLException ex) {
    throw new MyAppException("Query failed.", ex);
  }
}

请注意,您不需要在块ResultSet内创建对象,因为当对象关闭时 's 会自动关闭。tryResultSetPreparedStatement

当生成它的 Statement 对象关闭、重新执行或用于从多个结果序列中检索下一个结果时,ResultSet 对象将自动关闭。

参考:https ://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

于 2017-04-06T17:05:41.500 回答
3

如果您使用的是 Java 7,则可以在那些实现AutoCloseable的类中使用异常处理机制的改进(即PreparedStatementResultset

您可能还会发现这个问题很有趣:Closing ResultSet in Java 7

于 2012-08-06T08:17:14.747 回答
1

我知道这是一个老问题,但如果有人正在寻找答案,java 现在有 try-with-resouce 解决方案。

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
于 2013-06-12T05:02:03.127 回答
0

不要省略调用关闭。它可能会导致问题。

我更喜欢在 finally 中添加 try/catch 块。

于 2008-11-26T17:10:13.653 回答
0

可能是一种旧的(虽然简单)的做事方式,但它仍然有效:

public class DatabaseTest {

    private Connection conn;    
    private Statement st;   
    private ResultSet rs;
    private PreparedStatement ps;

    public DatabaseTest() {
        // if needed
    }

    public String getSomethingFromDatabase(...) {
        String something = null;

        // code here

        try {
            // code here

        } catch(SQLException se) {
            se.printStackTrace();

        } finally { // will always execute even after a return statement
            closeDatabaseResources();
        }

        return something;
    }

    private void closeDatabaseResources() {
        try {
            if(conn != null) {
                System.out.println("conn closed");
                conn.close();
            }

            if(st != null) {
                System.out.println("st closed");
                st.close();
            }

            if(rs != null) {
                System.out.println("rs closed");
                rs.close();
            }

            if(ps != null) {
                System.out.println("ps closed");
                ps.close();
            }

        } catch(SQLException se) {
            se.printStackTrace();
        }               
    }
}
于 2014-12-08T05:43:50.140 回答
-1

关注 finally 子句,

finally {
   try {
      rs.close();
      ps.close();
   } catch (Exception e) {
      // Do something
   }
}

我认为你必须修改2点。

首先,在fainlly 子句中再次使用try & catch。

其次,在执行 ps.close() 之前执行 rs.close()。

fly1997@naver.com

于 2012-08-06T08:06:38.103 回答
-2

我用这个。。

finally
{
    if (ps != null) ps.close();
    if (rs != null) rs.close();
}
于 2008-11-26T17:11:00.720 回答