Результирующий набор не закрывается при закрытии соединения?

StackOverflow https://stackoverflow.com/questions/103938

  •  01-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Были ли у вас проблемы с тем, что результирующие наборы и инструкции не закрывались при закрытии соединения?

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

Это было полезно?

Решение

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

Другие советы

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

"ORA-01000: maximum open cursors exceeded"

Итак:Всегда закрывайте свой набор результатов!

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

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

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

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

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

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

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

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

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

Пример:

<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>

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top