Результирующий набор не закрывается при закрытии соединения?
Вопрос
Я проводил проверку кода (в основном с использованием таких инструментов, как 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
- Сервер выбирает в базе данных 1
- Сервер "закрывает" соединение с базой данных 1
- Сервер подключается к базе данных 2
- Зашли в тупик!
Это произошло по двум причинам: мы столкнулись с гораздо большим объемом трафика, чем обычно, и спецификация 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(), есть причина вызвать его, и игнорировать его можно на свой страх и риск.