ResultSet не закрывается, когда соединение закрыто? - PullRequest
49 голосов
/ 19 сентября 2008

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

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

Ответы [ 8 ]

51 голосов
/ 19 сентября 2008

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

28 голосов
/ 19 сентября 2008

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

"ORA-01000: maximum open cursors exceeded"

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

19 голосов
/ 19 сентября 2008

Вы всегда должны явно закрывать все ресурсы 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) {
        }
    }
}
9 голосов
/ 19 сентября 2008

В этом случае 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) и т. Д., Не беспокоясь об исключениях, которые якобы выдают при закрытом сканировании. .

8 голосов
/ 20 сентября 2008

Я работаю в большой веб-среде 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>
8 голосов
/ 19 сентября 2008

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

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

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

Что лучше всего закрывать все это.

4 голосов
/ 19 сентября 2008

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

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

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

4 голосов
/ 19 сентября 2008

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

...