虽然Connection之后关闭,但必须单独关闭JDBC结果集和语句吗?

时间:2010-12-22 08:36:14

标签: java jdbc

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

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

问题是连接的关闭是否完成了工作,或者是否正在使用某些资源。

12 个答案:

答案 0 :(得分:180)

你所做的是完美和非常好的练习。

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

所以你不能总是依靠connection.close()来清理。

我希望这会有所帮助:)

答案 1 :(得分:109)

由于try-with-resources statement,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.
    }
}

这种语法非常简洁和优雅。即使无法创建connectionstatement也会被关闭。

答案 2 :(得分:65)

来自javadocs

  

Statement对象关闭时,它   当前ResultSet对象,如果有的话   存在,也是关闭的。

但是,关闭基础Statement时,javadocs关于ResultSetConnection是否已关闭并不是很明确。他们只是声明关闭连接:

  

发布此Connection个对象   数据库和JDBC资源   马上而不是等待   它们会被自动释放。

在我看来,当你完成它们时,总是明确地关闭ResultSetsStatementsConnections,因为close的实现可能因数据库驱动程序而异。

您可以使用Apache中的DBUtils中的closeQuietly等方法为自己节省大量的样板代码。

答案 3 :(得分:35)

我现在正在使用Oracle和Java。在这里我的观点是:

您应该明确关闭ResultSetStatement,因为Oracle之前在关闭连接后保持游标保持打开存在问题。如果不关闭ResultSet(光标),则会抛出超出最大打开游标数之类的错误。

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

这是教程Close ResultSet when finished

  

完成后关闭ResultSet

     

完成后立即关闭ResultSet个对象   甚至与ResultSet对象一起工作   虽然Statement对象关闭了   ResultSet隐含的对象   关闭,明确关闭ResultSet   给垃圾收集器机会   尽早回忆记忆   因为ResultSet对象可能占用   大量内存取决于查询。

     

ResultSet.close();

答案 4 :(得分:6)

如果您想要更紧凑的代码,我建议使用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);
}

答案 5 :(得分:3)

关闭与JDBC相关的资源的正确且安全的方法(取自How to Close JDBC Resources Properly – Every Time):

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

答案 6 :(得分:2)

Connection是否可合并无关紧要。即使是可池化的连接也必须在返回池之前进行清理。

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

答案 7 :(得分:0)

不,你不需要关闭任何东西,但连接。根据JDBC规范,关闭任何更高的对象将自动关闭较低的对象。关闭Connection将关闭该连接创建的所有Statement。关闭任何Statement将关闭由ResultSet创建的所有StatementConnection是否可以使用并不重要。即使是poolable连接也必须在返回游泳池之前进行清洁。

当然,您可能在Connection上创建了很多嵌套循环来创建大量语句,然后关闭它们是合适的。我几乎从未关闭ResultSet但是,当关闭StatementConnection关闭它们时似乎过多。

答案 8 :(得分:0)

一些便利功能:

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

答案 9 :(得分:0)

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

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

答案 10 :(得分:-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%完美,因为在检查关闭状态和关闭本身之间,状态的变化有一点空间。在最坏的情况下,你会得到一个警告。 - 但它比长期查询中状态更改的可能性要小。我们在生产中使用这种模式,带有“avarage”负载(150同时用户),我们没有遇到任何问题 - 所以永远不要看到这个警告信息。

答案 11 :(得分:-1)

据我所记得,在当前的JDBC中,结果集和语句实现了AutoCloseable接口。这意味着它们一旦被破坏或超出范围就会自动关闭。