在哪里关闭java PreparedStatements和ResultSet?

在哪里关闭java PreparedStatements和ResultSet?,java,jdbc,resource-management,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 MyAppExcepti

以代码为例:

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子句中?或者干脆不麻烦调用close?

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


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

请勿忽略调用close。这可能会引起问题


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

如果您真的要手动运行自己的jdbc,它肯定会变得混乱。finally中的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); 
}
我用这个

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

我通常有一个实用方法,可以关闭类似这样的东西,包括注意不要尝试使用空引用执行任何操作

通常,如果
close()
抛出异常,我实际上并不关心,所以我只是记录异常并将其吞下——但另一种选择是将其转换为
运行时异常。无论采用哪种方式,我都建议使用一种易于调用的实用方法,因为在许多地方您可能需要这样做


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

不要浪费时间编码低级异常管理,请使用更高级的API(如Spring JDBC)或连接/语句/rs对象的自定义包装器,要隐藏凌乱的try-catch-rided代码。

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

在Java7之前,最好使用嵌套的finally块,而不是测试空引用

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

Connection db = ds.getConnection();
try {
  PreparedStatement ps = ...;
  try {
    ResultSet rs = ...
    try {
      ...
    }
    finally {
      rs.close();
    }
  } 
  finally {
    ps.close();
  }
} 
finally {
  db.close();
}
另请注意:

关闭语句对象时,其当前ResultSet对象(如果存在)也将关闭

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

focus finally子句

finally {
   try {
      rs.close();
      ps.close();
   } catch (Exception e) {
      // Do something
   }
}
我认为你必须修改两点

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

第二,在执行ps.close()之前先执行rs.close()


fly1997@naver.com

如果您使用的是Java 7,您可以在实现的类中使用异常处理机制的改进(即
PreparedStatement
Resultset


您可能还会发现这个问题很有趣:

我知道这是一个老问题,但为了防止有人在寻找答案,java现在有了“资源试用”解决方案

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
可能是一种古老(虽然简单)的做事方式,但它仍然有效:

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

基于@erickson的答案,为什么不在一个
块中尝试这样做呢

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);
  }
}
请注意,您不需要在
try
块中创建
ResultSet
对象,因为当
PreparedStatement
对象关闭时,
ResultSet
会自动关闭

语句对象关闭时,ResultSet对象将自动关闭 生成它的文件将被关闭、重新执行或用于检索下一个文件 由多个结果序列生成的结果


参考:

这并不能回答问题-因为ps.close()和rs.close()都可以抛出SqlException。如果将finally放在与右大括号相同的行上,那么就不会那么难看了。;)Ctrl-Shift-F,让您心满意足!;)如果不关闭语句,这是一个坏主意。如果您使用连接池,您的代码将以
ORA-01000:超出最大打开游标数而告终
。关闭语句有帮助,因为Oracles通用连接池(UCP)也有语句池。@没有人建议可以避免关闭语句。您在响应什么?@roomsg这是真的,但是如果您使用的是
PreparedStatement
,那么
ResultSet
对象很可能是在循环中创建的,因此确保它们像这样关闭仍然是很好的代码卫生。如果在关闭准备好的语句之前创建了大量结果集,则仍然可能存在问题,即使它们最终将被关闭。如果您自己滚动,此解决方案效果非常好,因为close方法引发的SQLExceptions无论如何都是不可恢复的。另一种干净利落的方式