ResultSet не закрывается, когда соединение закрыто?

Я занимался проверкой кода (в основном с использованием таких инструментов, как FindBugs) одного из наших любимых проектов, и FindBugs пометил следующий код как ошибочный (псевдокод):

Connection conn = dataSource.getConnection();

try{
    PreparedStatement stmt = conn.prepareStatement();
    //initialize the statement
    stmt.execute();
    ResultSet rs =  stmt.getResultSet();
    //get data
}finally{
    conn.close();
}

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

finally{
    try{
        rs.close()
    }catch(SqlException se){
        //log it
    }
    try{
        stmt.close();
    }catch(SqlException se){
        //log it
    }
    conn.close();
}

Но я сталкивался с вышеупомянутым паттерном во многих проектах (от довольно многих компаний), и никто не закрывал ResultSets или Statementments.

Были ли у вас проблемы с закрытием ResultSets и выписок при закрытии соединения?

Я нашел только это, и это относится к Oracle, имеющему проблемы с закрытием ResultSets при закрытии Connections (мы используем Oracle db, отсюда и мои исправления). java.sql.api ничего не говорит в javadoc Connection.close().

8 ответов

Решение

Одна проблема с ТОЛЬКО закрытием соединения, а не с набором результатов, заключается в том, что если ваш код управления соединением использует пул соединений, connection.close() просто вернул бы соединение обратно в пул. Кроме того, некоторые базы данных имеют ресурс курсора на сервере, который не будет освобожден должным образом, если он явно не закрыт.

У меня были проблемы с незамкнутыми ResultSets в Oracle, хотя соединение было закрыто. Ошибка, которую я получил, была

"ORA-01000: maximum open cursors exceeded"

Итак: всегда закрывайте свой ResultSet!

Вы всегда должны явно закрывать все ресурсы JDBC. Как уже сказали Аарон и Джон, закрытие соединения часто только возвращает его в пул, и не все драйверы JDBC реализуются одинаково.

Вот служебный метод, который можно использовать из блока finally:

public static void closeEverything(ResultSet rs, Statement stmt,
        Connection con) {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
        }
    }
    if (stmt != null) {
        try {
            stmt.close();
        } catch (SQLException e) {
        }
    }
    if (con != null) {
        try {
            con.close();
        } catch (SQLException e) {
        }
    }
}

В этом случае Oracle выдаст вам ошибки об открытых курсорах.

Согласно: http://java.sun.com/javase/6/docs/api/java/sql/Statement.html

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

Все эти детали оставлены на усмотрение поставщика драйверов JDBC.

Всегда безопаснее закрыть все явно. Мы написали класс util, который упаковывает все с помощью try{ xxx } catch (Throwable {}), так что вы можете просто вызывать Utils.close (rs) и Utils.close (stmt) и т. Д., Не беспокоясь об исключениях, которые якобы выдают при закрытом сканировании.,

Я работаю в большой веб-среде J2EE. У нас есть несколько баз данных, к которым можно подключиться одним запросом. Мы начали получать логические тупики в некоторых наших приложениях. Проблема заключалась в следующем:

  1. Пользователь будет запрашивать страницу
  2. Сервер подключается к БД 1
  3. Сервер выбирает на БД 1
  4. Сервер "закрывает" соединение с БД 1
  5. Сервер подключается к БД 2
  6. В тупике!

Это произошло по двум причинам: мы испытывали намного больший объем трафика, чем обычно, и спецификация J2EE по умолчанию фактически не закрывает ваше соединение, пока поток не завершит выполнение. Таким образом, в вышеприведенном примере шаг 4 никогда фактически не закрывал соединение, даже если они были закрыты должным образом.

Чтобы это исправить, вы должны использовать ссылки на ресурсы в файле web.xml для ваших соединений с базой данных, а также установить для res-shared-scope значение unharable.

Пример:

<resource-ref>
    <description>My Database</description>
    <res-ref-name>jdbc/jndi/pathtodatasource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>

Мост ODBC может вызвать утечку памяти с некоторыми драйверами ODBC.

Если вы используете хороший драйвер JDBC, у вас не должно возникнуть проблем с закрытием соединения. Но есть 2 проблемы:

  • Знаете ли вы, если у вас есть хороший водитель?
  • Будете ли вы использовать другие драйверы JDBC в будущем?

Это лучшая практика, чтобы закрыть все это.

Я определенно видел проблемы с незакрытыми ResultSets, и что может мешать закрывать их все время, верно? Ненадежность необходимости помнить об этом - одна из лучших причин для перехода к платформам, которые управляют этими деталями для вас. Это может оказаться невозможным в вашей среде разработки, но мне очень повезло, используя Spring для управления транзакциями JPA. Грязные детали открытия соединений, операторов, наборов результатов и написания слишком сложных блоков try/catch/finally (с блоками try / catch в блоке finally!), Чтобы снова закрыть их, просто исчезают, оставляя вам фактически выполнить некоторую работу., Я очень рекомендую перейти на такое решение.

В Java операторы (не Resultsets) соотносятся с курсорами в Oracle. Лучше закрыть ресурсы, которые вы открываете, так как может произойти непредвиденное поведение в отношении JVM и системных ресурсов.

Кроме того, некоторые инфраструктуры пулов JDBC объединяют операторы и соединения, поэтому их закрытие может не пометить эти объекты как свободные в пуле и вызвать проблемы с производительностью в среде.

В общем, если на объекте есть метод close() или destroy(), есть причина вызвать его, и игнорировать его можно на свой страх и риск.

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