Должны ли JDBC Resultsets и Statement быть закрыты отдельно, хотя впоследствии было закрыто Соединение?
Говорят, что это хорошая привычка - закрывать все ресурсы 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) {};
}
Вопрос в том, выполняет ли закрытие соединения работу или оставляет какие-то ресурсы в использовании.
8 ответов
То, что вы сделали, является идеальной и очень хорошей практикой.
Причина, по которой я говорю, что это хорошая практика... Например, если по какой-то причине вы используете "примитивный" тип пула баз данных и вызываете connection.close()
соединение будет возвращено в пул и ResultSet
/Statement
никогда не будет закрыт, и тогда вы столкнетесь с множеством новых проблем!
Так что вы не всегда можете рассчитывать на connection.close()
очистить.
Надеюсь, это поможет:)
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
не может быть создано.
Из Javadocs:
Когда
Statement
объект закрыт, его токResultSet
Объект, если таковой существует, также закрыт.
Тем не менее, Javadocs не очень ясно, является ли Statement
а также ResultSet
закрыты, когда вы закрываете основной Connection
, Они просто заявляют, что закрытие соединения:
Выпускает это
Connection
база данных объекта и ресурсы JDBC немедленно вместо ожидания их автоматического освобождения.
На мой взгляд, всегда явно близко ResultSets
, Statements
а также Connections
когда вы закончите с ними в качестве реализации close
может варьироваться между драйверами базы данных.
Вы можете сэкономить много кода, используя такие методы, как closeQuietly
в DBUtils от Apache.
Я сейчас использую Oracle с Java. Вот моя точка зрения:
Вы должны закрыть ResultSet
а также Statement
явно потому, что у Oracle ранее были проблемы с сохранением курсоров открытыми даже после закрытия соединения. Если вы не закроете ResultSet
(курсор) будет выдано сообщение об ошибке "Превышено максимальное количество открытых курсоров".
Я думаю, что вы можете столкнуться с той же проблемой с другими базами данных, которые вы используете.
Вот учебник Закрыть ResultSet, когда закончите:
Закрыть ResultSet, когда закончите
близко
ResultSet
объект, как только вы закончите работать сResultSet
возражать, хотяStatement
объект закрываетResultSet
объект неявно, когда он закрывается, закрываяResultSet
явно дает возможность сборщику мусора вспомнить память как можно раньше, потому чтоResultSet
Объект может занимать много памяти в зависимости от запроса.
ResultSet.close();
Если вы хотите более компактный код, я предлагаю использовать 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);
}
Нет, вам не нужно ничего закрывать, НО соединение. Согласно спецификациям JDBC, закрытие любого более высокого объекта автоматически закроет более низкие объекты. закрытие Connection
закроет любой Statement
Это соединение создано. Закрытие любого Statement
закроет все ResultSet
с, которые были созданы этим Statement
, Не имеет значения, если Connection
можно объединять или нет. Даже объединяемое в пул соединение необходимо очистить перед возвращением в бассейн.
Конечно, вы можете иметь длинные вложенные циклы на Connection
создавать множество утверждений, а затем закрывать их. Я почти никогда не закрываюсь ResultSet
хотя, кажется чрезмерным при закрытии Statement
или же Connection
ЗАКЛЮЧИТЕ их.
Не имеет значения, если Connection
можно объединять или нет. Даже соединение с бассейном должно быть очищено перед возвращением в бассейн.
"Очистить" обычно означает закрытие наборов результатов и откат любых ожидающих транзакций, но не закрытие соединения, в противном случае пул теряет смысл.
Правильный и безопасный метод для закрытия ресурсов, связанных с JDBC, это (взято из Как правильно закрыть ресурсы JDBC - каждый раз):
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();
}
Я создал следующий метод для создания многоразового вкладыша One Liner:
public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
if (!resultSet.isClosed()) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
if (!statement.isClosed()) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
if (!connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Я использую этот код в родительском классе, который унаследован от всех моих классов, отправляющих запросы к БД. Я могу использовать Oneliner для всех запросов, даже если у меня нет resultSet. Метод заботится о закрытии ResultSet, Statement, Connection в правильном порядке. Так выглядит мой блок finally.
finally {
oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}
С формой 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%, потому что между проверкой состояния закрытия и самим закрытием остается мало места для изменения состояния. В худшем случае вы получите предупреждение в течение длительного времени. - но это меньше, чем возможность изменения состояния в долгосрочных запросах. Мы используем этот шаблон в производстве с "средней" нагрузкой (150 одновременных пользователей), и у нас не было никаких проблем с ним - поэтому никогда не видели это предупреждающее сообщение.
Некоторые удобные функции:
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);
}
Насколько я помню, в текущем JDBC Resultsets и операторы реализуют интерфейс AutoCloseable. Это означает, что они автоматически закрываются после уничтожения или выхода из области видимости.