尽管随后关闭了连接,是否必须分别关闭JDBC结果集和语句?


256

据说在使用后关闭所有JDBC资源是一个好习惯。但是,如果我有以下代码,是否有必要关闭结果集和语句?

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    try { if (rs != null) rs.close(); } catch (Exception e) {};
    try { if (stmt != null) stmt.close(); } catch (Exception e) {};
    try { if (conn != null) conn.close(); } catch (Exception e) {};
}

问题是连接的关闭是否可以完成工作,或者是否使某些资源处于使用状态。


Answers:


199

您所做的是完美且非常好的做法。

我说这是一个好的做法的原因...例如,如果由于某种原因您正在使用“原始”类型的数据库池并调用connection.close(),则连接将返回到池中,并且ResultSet/ Statement将永远不会关闭,然后您会遇到许多不同的新问题!

因此,您不能总是指望connection.close()进行清理。

我希望这有帮助 :)


4
...以及最明显的原因明确关闭所有内容的原因。
Zeemee 2010年

2
我同意关闭结果集和语句是一种好习惯。但是,结果集和语句是垃圾收集的-它们不会永远保持打开状态,并且您不会“遇到许多不同的新问题”。
stepanian 2011年

3
@Ralph Stevens-您不能指望这一点。我曾经遇到过MSSQL JDBC驱动程序泄漏内存的情况,因为即使关闭了ResultSet,也没有关闭它们。
保罗

7
@Paul-有趣。在我看来,这听起来像JDBC驱动程序的缺点。
stepanian 2011年

2
@tleb-会按预期工作。尽管从理论上讲,例外是“昂贵的”,所以会有很小的性能下降(您已经确定)
Paul

124

借助try-with-resources语句, Java 1.7使我们的生活更加轻松。

try (Connection connection = dataSource.getConnection();
    Statement statement = connection.createStatement()) {
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do stuff with the result set.
    }
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do more stuff with the second result set.
    }
}

此语法非常简短而优雅。而且connection的确会即使在关闭时,statement不能创建。


56
您不需要像这样嵌套,您可以在一个try-with-resources中完成所有操作,只需将资源声明视为单独的语句(由分隔;
Mark Rotteveel 2013年

2
马克·罗特维(Mark Rotteveel):您可以对所有三个连接,语句和结果集使用一次尝试,但是如果要执行多个查询,则必须在开始新查询之前关闭先前的结果集。至少那是我使用的DBMS的工作方式。
劳尔·萨利纳斯-蒙塔古多2015年

你为什么不做这样的事情?try(开放式连接){try(多个语句和结果集){},尤其是当下一个查询结果可以与前一个查询一起计算时。
Daniel Hajduk

丹尼尔:当我使用该模式时,底层的JDBC后端不支持保持ResultSet打开并打开第二个ResultSet。
2015年

拉斯乔(Racio),您可以在捕获区中做任何您想做的事
劳尔·萨利纳斯·

73

javadocs

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

然而,的Javadoc不是很清楚是否在StatementResultSet当您关闭底层被关闭Connection。他们只是声明关闭一个Connection:

立即释放该Connection对象的数据库和JDBC资源,而不是等待它们自动释放。

在我看来,始终显式关闭ResultSetsStatements并且Connections在完成它们后,由于close数据库驱动程序的实现可能会有所不同。

您可以通过使用诸如节省大量的锅炉板代码closeQuietlyDBUtils Apache的。


1
谢谢dogbane。关键是您不能依赖Connection.close的实现,对吗?
Zeemee 2010年


39

我现在将Oracle与Java结合使用。这是我的观点:

您应该明确地关闭ResultSetStatement因为Oracle以前在关闭连接后仍存在打开游标的问题。如果不关闭ResultSet(光标),它将抛出错误,例如超出了最大打开游标数

我认为您使用的其他数据库可能遇到相同的问题。

这是教程完成后关闭ResultSet

完成后关闭ResultSet

关闭ResultSet对象尽快使用完的工作ResultSet,即使对象Statement对象关闭 ResultSet对象隐含当它关闭,关闭ResultSet明确地给出了机会垃圾收集重拾记忆尽早因为ResultSet对象可能会占用大量的根据查询内存。

ResultSet.close();


感谢hilal,这些都是尽早关闭它的充分理由。但是,是否在连接之前直接关闭ResultSet和Statement是否重要(这在某些情况下:不及早)?
Zeemee 2010年

如果关闭连接,它将同时关闭所有结果集ans语句,但是您应该在连接之前关闭结果集

并且为什么我应该在连接之前关闭结果集?您的意思是因为oracle驱动程序问题?
Zeemee 2010年

1
这是更一般的说明:) stackoverflow.com/questions/103938/...

从理论上讲,如果您关闭语句,则不会 具备关闭结果集,但它可能是很好的做法。
rogerdpack

8

如果您想要更紧凑的代码,建议您使用Apache Commons DbUtils。在这种情况下:

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
}

3
如果我使用此代码而不是rs.close(),stmt.close(),conn.close(),将会发生什么
Onkar Musale

3

正确和安全的方法来关闭与JDBC相关的资源(此方法取自“ 如何正确地关闭JDBC资源-每次”):

Connection connection = dataSource.getConnection();
try {
    Statement statement = connection.createStatement();

    try {
        ResultSet resultSet = statement.executeQuery("some query");

        try {
            // Do stuff with the result set.
        } finally {
            resultSet.close();
        }
    } finally {
        statement.close();
    }
} finally {
    connection.close();
}

3

是否Connection可合并都没有关系。即使是可池化的连接也必须在返回池之前进行清理。

“清除”通常意味着关闭结果集并回滚任何未决的事务,但不关闭连接。否则,池将失去其意义。


2

不,您不需要关闭任何东西,但可以连接。根据JDBC规范,关闭任何较高的对象将自动关闭较低的对象。关闭Connection将关闭Statement该连接创建的所有。关闭any Statement将关闭ResultSet该对象创建的所有Statement。是否Connection可合并都没有关系。即使是可池化的连接也必须在返回池之前进行清理。

当然,在Connection创建大量语句时,您可能会有很长的嵌套循环,然后关闭它们是适当的。我几乎从不关闭ResultSet,在关闭StatementConnection将它们关闭时似乎过多。


1

我创建了以下方法来创建可重复使用的“一线”:

public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            if (!resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            if (!statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if (connection != null) {
        try {
            if (!connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

我在继承给我所有发送DB查询的类的父类中使用此代码。即使我没有resultSet我也可以在所有查询上使用Oneliner。该方法负责按正确的顺序关闭ResultSet,Statement,Connection。这就是我的finally块的样子。

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}


-1

一些便利功能:

public static void silentCloseResultSets(Statement st) {
    try {
        while (!(!st.getMoreResults() && (st.getUpdateCount() == -1))) {}
    } catch (SQLException ignore) {}
}
public static void silentCloseResultSets(Statement ...statements) {
    for (Statement st: statements) silentCloseResultSets(st);
}

这里没有什么可以关闭任何东西。只是浪费时间读取整个响应的无意义循环,即使显然不再需要它也是如此。
罗恩侯爵

-1

对于Java 6表单,我认为最好在关闭前检查它是否已关闭(例如,如果某个连接池合并程序退出了其他线程中的连接),例如某些网络问题,则可以关闭语句和结果集状态。(这种情况很少发生,但是我在Oracle和DBCP中遇到了这个问题)。我的模式是这样的(使用旧的Java语法)是:

try {
    //...   
    return resp;
} finally {
    if (rs != null && !rs.isClosed()) {
        try {
            rs.close();
        } catch (Exception e2) { 
            log.warn("Cannot close resultset: " + e2.getMessage());
        }
    }
    if (stmt != null && !stmt.isClosed()) {
        try {
            stmt.close();
        } catch (Exception e2) {
            log.warn("Cannot close statement " + e2.getMessage()); 
        }
    }
    if (con != null && !conn.isClosed()) {
        try {
            con.close();
        } catch (Exception e2) {
            log.warn("Cannot close connection: " + e2.getMessage());
        }
    }
}

从理论上讲,它不是100%完美的,因为在检查关闭状态和关闭本身之间,状态变化的空间很小。在最坏的情况下,您将长时间收到警告。-但比长期查询中状态更改的可能性要小。我们在生产中使用这种模式,并具有“可用”负载(150个同时用户),并且它没有问题-因此永远不会看到该警告消息。


您不需要isClosed()测试,因为关闭所有已经关闭的测试都是禁止操作的。消除了计时窗口问题。也可以通过制作Connection, StatementResultSet局部变量来消除这种情况。
罗恩侯爵
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.