Должны ли JDBC Resultsets и Statement быть закрыты отдельно, хотя впоследствии было закрыто Соединение?

Говорят, что это хорошая привычка - закрывать все ресурсы 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) {};
}

Вопрос в том, выполняет ли закрытие соединения работу или оставляет какие-то ресурсы в использовании.

8 ответов

Решение

То, что вы сделали, является идеальной и очень хорошей практикой.

Причина, по которой я говорю, что это хорошая практика... Например, если по какой-то причине вы используете "примитивный" тип пула баз данных и вызываете connection.close()соединение будет возвращено в пул и ResultSet/Statement никогда не будет закрыт, и тогда вы столкнетесь с множеством новых проблем!

Так что вы не всегда можете рассчитывать на connection.close() очистить.

Надеюсь, это поможет:)

Java 1.7 делает нашу жизнь намного проще благодаря выражению try-with-resources.

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 не может быть создано.

Из Javadocs:

Когда Statement объект закрыт, его ток ResultSet Объект, если таковой существует, также закрыт.

Тем не менее, Javadocs не очень ясно, является ли Statement а также ResultSet закрыты, когда вы закрываете основной Connection, Они просто заявляют, что закрытие соединения:

Выпускает это Connection база данных объекта и ресурсы JDBC немедленно вместо ожидания их автоматического освобождения.

На мой взгляд, всегда явно близко ResultSets, Statements а также Connections когда вы закончите с ними в качестве реализации close может варьироваться между драйверами базы данных.

Вы можете сэкономить много кода, используя такие методы, как closeQuietly в DBUtils от Apache.

Я сейчас использую Oracle с Java. Вот моя точка зрения:

Вы должны закрыть ResultSet а также Statement явно потому, что у Oracle ранее были проблемы с сохранением курсоров открытыми даже после закрытия соединения. Если вы не закроете ResultSet (курсор) будет выдано сообщение об ошибке "Превышено максимальное количество открытых курсоров".

Я думаю, что вы можете столкнуться с той же проблемой с другими базами данных, которые вы используете.

Вот учебник Закрыть ResultSet, когда закончите:

Закрыть ResultSet, когда закончите

близко ResultSet объект, как только вы закончите работать с ResultSet возражать, хотя Statement объект закрывает ResultSet объект неявно, когда он закрывается, закрывая ResultSet явно дает возможность сборщику мусора вспомнить память как можно раньше, потому что ResultSet Объект может занимать много памяти в зависимости от запроса.

ResultSet.close();

Если вы хотите более компактный код, я предлагаю использовать 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);
}

Нет, вам не нужно ничего закрывать, НО соединение. Согласно спецификациям JDBC, закрытие любого более высокого объекта автоматически закроет более низкие объекты. закрытие Connection закроет любой StatementЭто соединение создано. Закрытие любого Statement закроет все ResultSetс, которые были созданы этим Statement, Не имеет значения, если Connection можно объединять или нет. Даже объединяемое в пул соединение необходимо очистить перед возвращением в бассейн.

Конечно, вы можете иметь длинные вложенные циклы на Connection создавать множество утверждений, а затем закрывать их. Я почти никогда не закрываюсь ResultSet хотя, кажется чрезмерным при закрытии Statement или же Connection ЗАКЛЮЧИТЕ их.

Не имеет значения, если Connection можно объединять или нет. Даже соединение с бассейном должно быть очищено перед возвращением в бассейн.

"Очистить" обычно означает закрытие наборов результатов и откат любых ожидающих транзакций, но не закрытие соединения, в противном случае пул теряет смысл.

Правильный и безопасный метод для закрытия ресурсов, связанных с 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();
}

Я создал следующий метод для создания многоразового вкладыша One Liner:

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

Я использую этот код в родительском классе, который унаследован от всех моих классов, отправляющих запросы к БД. Я могу использовать Oneliner для всех запросов, даже если у меня нет resultSet. Метод заботится о закрытии ResultSet, Statement, Connection в правильном порядке. Так выглядит мой блок finally.

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

С формой 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 одновременных пользователей), и у нас не было никаких проблем с ним - поэтому никогда не видели это предупреждающее сообщение.

Некоторые удобные функции:

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

Насколько я помню, в текущем JDBC Resultsets и операторы реализуют интерфейс AutoCloseable. Это означает, что они автоматически закрываются после уничтожения или выхода из области видимости.

Другие вопросы по тегам