如何在Java中正确清理JDBC资源?

时间:2010-12-22 10:22:08

标签: java jdbc

清理JDBC资源时最佳做法是什么?为什么?我保持示例简短,因此只是清理ResultSet。

finally
{
  if(rs != null)
    try{ rs.close(); } catch(SQLException ignored) {}
}

finally
{
  try{ rs.close(); } catch(Exception ignored) {}
}

我个人赞成第二种选择,因为它有点短。对此的任何意见都非常感谢。

9 个答案:

答案 0 :(得分:10)

如今,JDK 7为您提供了清理资源的最简单方法:

String query = "select COF_NAME, PRICE from COFFEES";
try (Statement stmt = con.createStatement()) {
    ResultSet rs = stmt.executeQuery(query);
    while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        float price = rs.getFloat("PRICE");
        System.out.println(coffeeName + ", "  + price);
    }
}

try语句确保在语句结束时关闭每个资源。见http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

答案 1 :(得分:4)

正如其他人所指出的,JDBC资源(语句,结果集等)很少null。如果是,那么您手上的更大的问题比NullPointerException更多。在这方面,NullPointerException将帮助提醒您JDBC驱动程序的严重问题。如果您的JDBC驱动程序实际上为您提供null引用,则在调用close()之前对null进行典型检查会无提示地隐藏问题。

同样,并非所有JDBC驱动程序都严格遵循规范。例如,某些驱动程序在关联ResultSet关闭时不会自动关闭Statement。因此,您必须确保明确关闭ResultSet Statement(叹息)。

在实践中,我发现这种技术很有用(尽管它不是最漂亮的):

PreparedStatement statement = connection.prepareStatement("...");
try {
    ResultSet results = statement.executeQuery();
    try {
        while (results.next()) {
            // ...
        }
    } finally {
        results.close();
    }
} finally {
    statement.close();
}

此技术可确保执行每个close()语句,从ResultSet开始并向外工作。如果驱动程序为您提供NullPointerException引用,仍然会抛出null,但我允许这样做是出于开头所解释的原因。如果任何SQLException语句失败,我仍然会抛出close()个(我认为这是件好事 - 我想知道是否出现问题)。

答案 2 :(得分:3)

我发现你的第二个(不常见)版本没问题。

  • 通常,rs不会是null,因此在极少数情况下会发生NPE。所以我认为这里没有性能问题。
  • rs = null
  • 的情况下,两个版本的行为完全相同

唯一的缺点 - 如果我们要关闭多个资源,那么我们必须为每个资源添加一个try / catch,如果我们想要关闭尽可能多的资源。否则,我们将使用第一个null输入catch子句,这可能会导致未泄漏的泄漏。

所以它看起来像那样:

finally {
   try{rs.close();  }catch(Exception ignored){}
   try{stmt.close();}catch(Exception ignored){}
   try{conn.close();}catch(Exception ignored){}
}

......这仍然是可读和可理解的。但是,根据永远不会改变一种常见的模式 - 我会坚持使用老式的方法来首先测试null并在关闭时捕捉SQLException

答案 3 :(得分:2)

我倾向于使用以下方法。我认为检查null是好的,因为它显示了你的意图,即你确实意识到在极少数情况下这些对象可能为空。 (空检查也比创建NullPointerException更快。)我也认为记录异常是好的,而不是吞下它们。在close失败的情况下,我想知道它并将其存储在我的日志文件中。

finally {            
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                 LOG.warn("Failed to close rs", e);
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) { 
                 LOG.warn("Failed to close st", e);     
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                 LOG.warn("Failed to close conn", e);
                }
            }
        }

如果您要经常这样做,而不是一遍又一遍地复制和粘贴此代码,请使用静态方法创建一个实用程序类来关闭ResultSet,Statement和Connection。

使用DBUtils,您可以非常简洁地执行此清理,如下所示:

 finally {            
            DBUtils.closeQuietly(rs);
            DBUtils.closeQuietly(st);
            DBUtils.closeQuietly(conn);            
        }

答案 4 :(得分:2)

public static void close(Statement... statements) {
        for (Statement stmt : statements) {
            try {
                if (stmt != null)
                    stmt.close();
            } catch (SQLException se) {
            }// nothing we can do
        }
    }

    public static void close(Connection conn) {
        try {
            if (conn != null)
                conn.close();
        } catch (SQLException se) {
        }// nothing we can do
    }
public static void close(ResultSet rs) {
        try {
            if (rs != null) 
                rs.close();
        } catch (SQLException se) {
        }// nothing we can do
}

答案 5 :(得分:1)

这是我对JDK 6的方法。如果你有JDK 7+,你最好使用我在这里描述的方法https://stackoverflow.com/a/9200053/259237

private void querySomething() {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet rs = null;
    try {
        // get connection
        // prepare statement
        // execute query
        // and so on
    } catch (SQLException e) {
        throw new MyException("Error while talking to database", e);
    } finally {
        close(connection, statement, rs);
    }
}

// useful because you probably have more than one method interacting with database
public static void close (Connection connection, Statement statement, ResultSet rs) {
    if (rs != null) {
        try { rs.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
    if (statement != null) {
        try { statement.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
    if (connection != null) {
        try { connection.close(); } catch (Exception e) { _logger.warning(e.toString()); }
    }
}
  • 很简短。
  • 它定义了一个可以静态导入的close方法。
  • 避免空挡块。
  • 它处理可能发生的任何SQLException(即使在getConnection或close方法中)。
  • 它是无效的。

答案 6 :(得分:0)

ResultSet rs = //initialize here
try {
  // do stuff here
} finally {
  try { rs.close(); }
  catch(SQLException ignored) {}
}

答案 7 :(得分:0)

如果您正在编写长时间运行的应用程序,则应考虑连接池。

Apache DBCP项目为您完成了很多这方面的工作。您还可以查看类似Spring JDBC或Hibernate的内容。

Spring的东西使用了对象池,并添加了一些非常好的方法来抽象出JDBC nastiness。

答案 8 :(得分:0)

protected void closeAll(){
        closeResultSet();
        closeStatement();
        closeConnection();
    }   

protected void closeConnection(){
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                /*Logger*/
            }
            connection = null;
        }
    }


    protected void closeStatement() {
        if (stmt != null) {
            try {
                ocstmt.close();
            } catch (SQLException e) {
                /*Logger*/
            }
            ocstmt = null;
        }
    }


    protected void closeResultSet() {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                /*Logger*/
            }
            rs = null;
        }
    }