Должны ли 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) {};
}
Вопрос: если закрытие соединения выполняет задание или если оно оставляет некоторые ресурсы в использовании.

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

Из javadocs:

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

Однако javadocs не очень четко понимают, закрыты ли теги Statement и ResultSet при закрытии базового Connection. Они просто заявляют, что закрывают соединение:

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

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

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

Ответ 4

Теперь я использую Oracle с Java. Здесь моя точка зрения:

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

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

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

Закрыть ResultSet при завершении

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

ResultSet.close();

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

С формой 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

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

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