在哪里关闭java PreparedStatements和ResultSet?

时间:2008-11-26 17:02:09

标签: java jdbc resource-management

考虑代码:

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子句中?或者只是不打扰打电话?

13 个答案:

答案 0 :(得分:44)

在Java 7中,您不应该显式关闭它们,而是使用automatic resource management确保关闭resources并正确处理异常。异常处理的工作方式如下:

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

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

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的引用。

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

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

答案 1 :(得分:25)

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

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

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

答案 2 :(得分:9)

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

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

答案 3 :(得分:7)

不要浪费时间编写低级异常管理,使用Spring-JDBC之类的高级API,或者连接/ statement / rs对象的自定义包装器来隐藏凌乱的try-catch驱动代码。 / p>

答案 4 :(得分:7)

另请注意:

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

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

仅在最终关闭PreparedStatement并且仅在它尚未关闭时才足够。如果你想要非常特别,请关闭ResultSet FIRST,而不是在关闭PreparedStatement之后(关闭它之后,就像这里的一些示例一样,实际上应该保证异常,因为它已经关闭)。

答案 5 :(得分:5)

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

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

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

答案 6 :(得分:2)

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

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

答案 7 :(得分:1)

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

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

答案 8 :(得分:0)

不要忽略呼叫关闭。它可能会导致问题。

我更喜欢将try / catch块添加到finally。

答案 9 :(得分: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();
        }               
    }
}

答案 10 :(得分:0)

Building on @erickson's answer, why not just do it in one try block like this?

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

Note that you don't need to create the ResultSet object inside the try block as ResultSet's are automatically closed when the PreparedStatement object is closed.

A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.

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

答案 11 :(得分:-1)

关注最后一句,

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

我认为你必须修改2分。

首先,使用try&在fainlly条款中再次捕获。

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

fly1997@naver.com

答案 12 :(得分:-2)

我用这个..

finally
{
    if (ps != null) ps.close();
    if (rs != null) rs.close();
}