Рассмотрим код:
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.createStatement(myQueryString);
rs = ps.executeQuery();
// process the results...
} catch (java.sql.SQLException e) {
log.error("an error!", e);
throw new MyAppException("I'm sorry. Your query did not work.");
} finally {
ps.close();
rs.close();
}
Вышеупомянутое не компилируется, потому что и PreparedStatement.close(), и ResultSet.close() выбрасывают java.sql.SQLException. Так можно ли добавить блок try / catch в предложение finally? Или переместить операторы закрытия в предложение try? Или просто не заморачиваться с близкими?




Для файлового ввода-вывода я обычно добавляю try / catch к блоку finally. Однако вы должны быть осторожны, чтобы не генерировать какие-либо исключения из блока finally, поскольку они приведут к потере исходного исключения (если таковое имеется).
См. Более конкретный пример закрытия соединения с базой данных в эта статья.
Не забывайте звонить близко. Это может вызвать проблемы.
Я предпочитаю добавлять блок try / catch в файл finally.
Если вы действительно вручную скручиваете свой собственный jdbc, он определенно становится беспорядочным. Close () в finally нужно обернуть собственным try catch, что, по крайней мере, некрасиво. Вы не можете пропустить закрытие, хотя ресурсы будут очищены при закрытии соединения (что может произойти не сразу, если вы используете пул). Фактически, одним из основных преимуществ использования фреймворка (например, спящего режима) для управления доступом к базе данных является управление подключением и обработкой набора результатов, чтобы вы не забыли закрыть.
Вы можете сделать что-нибудь такое простое, что хотя бы скроет беспорядок и гарантирует, что вы что-то не забудете.
public static void close(ResultSet rs, Statement ps, Connection conn)
{
if (rs!=null)
{
try
{
rs.close();
}
catch(SQLException e)
{
logger.error("The result set cannot be closed.", e);
}
}
if (ps != null)
{
try
{
ps.close();
} catch (SQLException e)
{
logger.error("The statement cannot be closed.", e);
}
}
if (conn != null)
{
try
{
conn.close();
} catch (SQLException e)
{
logger.error("The data source connection cannot be closed.", e);
}
}
}
а потом,
finally {
close(rs, ps, null);
}
Обратите внимание, что согласно JavaDoc for Statement ResultSet закрывается как побочный эффект закрытия оператора, поэтому в этом примере это не является строго необходимым (но не повредит). java.sun.com/javase/6/docs/api/java/sql/Statement.html#close ()
Я написал это так, потому что полезно иметь возможность гибко закрывать некоторые объекты как null. Таким образом, вы можете использовать ту же функцию, чтобы закрыть любое подмножество объектов (оператор, набор результатов, соединение).
Операторы всегда закрывать, оставляя их открытыми после закрытия соединения, могут вызвать проблемы: подробнее см. stackoverflow.com/questions/321418/….
Вы не должны игнорировать исключения из закрытия ресурса. В случае операции JDBC, например, это означает, что все, что вы делали внутри своего блока try, вероятно, на самом деле не произошло. Подход, принятый реализацией ARM, является образцовым: выбросить исключение close(), если оно единственное, в противном случае сохранить его, привязав к основному исключению.
Просто взглянул на это еще раз - это очень старый ответ, и он был написан в дни Java6. Вероятно, вы могли бы написать это немного более аккуратно в Java7, используя AutoCloseables, если соответствующие объекты поддерживают это.
@ СтивБ. Спасибо, мне было интересно, у Эриксона ниже есть ответ с использованием AutoCloseables. Хорошо почистил мой устаревший код. Поскольку ваш ответ принят, вы можете отредактировать его, чтобы зрители видели его :)
Использую 7, но мне это нравится, спасибо @SteveB. Просто и понятно.
Пользуюсь этим ..
finally
{
if (ps != null) ps.close();
if (rs != null) rs.close();
}
Это не отвечает на вопрос, потому что ps.close () и rs.close () могут генерировать SqlException.
Обычно у меня есть служебный метод, который может закрывать такие вещи, в том числе стараюсь не делать ничего с нулевой ссылкой.
Обычно, если close() выдает исключение, мне все равно, поэтому я просто регистрирую исключение и проглатываю его, но другой альтернативой было бы преобразовать его в RuntimeException. В любом случае, я рекомендую делать это с помощью служебного метода, который легко вызвать, поскольку вам может понадобиться сделать это во многих местах.
Обратите внимание, что ваше текущее решение не закроет ResultSet, если закрыть PreparedStatement не удается - лучше использовать вложенные блоки finally.
Не тратьте зря время на кодирование низкоуровневого управления исключениями, используйте высокоуровневый API, такой как Spring-JDBC, или настраиваемую оболочку вокруг объектов connection / statement / rs, чтобы скрыть беспорядочный код, связанный с try-catch.
Я тоже с этим согласен. Spring превратит проверенное исключение в непроверенное, потому что в большинстве случаев приложение не может восстановиться из исключений БД.
В Java 7 вы не должны закрывать их явно, но используйте автоматическое управление ресурсами, чтобы гарантировать, что Ресурсы закрыты, а исключения обрабатываются соответствующим образом. Обработка исключений работает так:
Exception in try | Exception in close | Result
-----------------+--------------------+----------------------------------------
No | No | Continue normally
No | Yes | Throw the close() exception
Yes | No | Throw the exception from try block
Yes | Yes | Add close() exception to main exception
| | as "suppressed", throw main exception
Надеюсь, это имеет смысл. В позволяет красивый код, например:
private void doEverythingInOneSillyMethod(String key)
throws MyAppException
{
try (Connection db = ds.getConnection()) {
db.setReadOnly(true);
...
try (PreparedStatement ps = db.prepareStatement(...)) {
ps.setString(1, key);
...
try (ResultSet rs = ps.executeQuery()) {
...
}
}
} catch (SQLException ex) {
throw new MyAppException("Query failed.", ex);
}
}
До Java 7 лучше использовать вложенные блоки finally, а не проверять ссылки на null.
Пример, который я покажу, может выглядеть некрасиво с глубоким вложением, но на практике хорошо спроектированный код, вероятно, не будет создавать соединение, оператор и результаты одним и тем же методом; часто каждый уровень вложенности включает передачу ресурса другому методу, который использует его как фабрику для другого ресурса. При таком подходе исключения из close() будут маскировать исключение изнутри блока try. Это можно преодолеть, но это приводит к еще более запутанному коду и требует специального класса исключений, который обеспечивает «подавленную» цепочку исключений, присутствующую в Java 7.
Connection db = ds.getConnection();
try {
PreparedStatement ps = ...;
try {
ResultSet rs = ...
try {
...
}
finally {
rs.close();
}
}
finally {
ps.close();
}
}
finally {
db.close();
}
Было бы менее уродливо, если бы вы поместили последний в той же строке, что и закрывающая фигурная скобка. ;)
Ctrl-Shift-F сколько душе угодно! ;)
Плохая идея, если вы не закрываете заявления. Если вы используете пулы соединений, ваш код будет иметь ORA-01000: maximum open cursors exceeded. Заключительные операторы помогают, потому что универсальный пул соединений Oracles (UCP) также выполняет объединение операторов.
@ceving Никто не предполагал, что закрывающих операторов можно избежать. На что вы отвечаете?
Закрытия PreparedStatement должно быть достаточно: базовый ResultSet закрывается при закрытии PreparedStatement. См .: docs.oracle.com/javase/7/docs/api/java/sql/…
@roomsg Это правда, но если вы используете PreparedStatement, объекты ResultSet, скорее всего, будут созданы внутри цикла, поэтому обеспечение их закрытия, как это, по-прежнему является хорошей гигиеной кода. Если вы создадите много наборов результатов перед закрытием подготовленного оператора, у вас все равно могут быть проблемы, даже если они в конечном итоге будут закрыты.
@erickson, правда, вы правы, ico циклы лучше обрабатывать / закрывать каждый набор результатов отдельно, но это не совсем применимо к данному примеру. Просто хотел уведомить всех, кто читает ваш (отличный!) Ответ.
Здесь есть помощь? :) stackoverflow.com/questions/35849664/…
Также обратите внимание:
«Когда объект Statement закрывается, его текущий объект ResultSet, если он существует, также закрывается».
http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close ()
Достаточно закрыть только PreparedStatement в finally, и только если он еще не закрыт. Если вы хотите быть действительно конкретным, закройте ResultSet ПЕРВЫМ, а не после закрытия PreparedStatement (закрытие его после, как некоторые из приведенных здесь примеров, должно фактически гарантировать исключение, поскольку оно уже закрыто).
Согласно документации, на которую вы ссылаетесь, вызов Statement.close () для уже закрытого оператора не имеет никакого эффекта. Многократное закрытие оператора не вызывает исключения, поэтому само собой разумеется, что выполнение того же действия для ResultSet должно быть столь же безобидным. Документация ResultSet явно не говорит об этом, но также не говорит, что вы не должны закрывать его более одного раза ... Весь смысл этой педантичной тирады в том, что он не ГАРАНТИРУЕТ исключения. Хотя было бы хорошо сначала закрыть ResultSet на случай, если какая-то реализация будет вести себя подобным образом.
Я ничего не могу с собой поделать. Я должен отметить, что это действительно полезный ответ, и тот, который выше говорит «используйте фреймворк», просто заставляет меня съеживаться.
предложение focus finally,
finally {
try {
rs.close();
ps.close();
} catch (Exception e) {
// Do something
}
}
Думаю, надо 2 пункта доработать.
Во-первых, снова используйте команду try & catch в предложении fainlly.
Во-вторых, выполните rs.close () перед выполнением ps.close ().
Если вы используете Java 7, вы можете использовать улучшения в механизмах обработки исключений в тех классах, которые реализуют Автоматическое закрытие (например, PreparedStatement, Resultset).
Вас также может заинтересовать этот вопрос: Закрытие ResultSet в Java 7
Я знаю, что это старый вопрос, но на всякий случай, если кто-то ищет ответ, у java теперь есть решение try-with-resouce.
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
Это то же самое, что и ответ Гвидо Гарсиа. Попытка с ресурсом требует, чтобы ресурс был автоматически закрываемым.
Вероятно, старый (хотя и простой) способ делать что-то, но он все еще работает:
public class DatabaseTest {
private Connection conn;
private Statement st;
private ResultSet rs;
private PreparedStatement ps;
public DatabaseTest() {
// if needed
}
public String getSomethingFromDatabase(...) {
String something = null;
// code here
try {
// code here
} catch(SQLException se) {
se.printStackTrace();
} finally { // will always execute even after a return statement
closeDatabaseResources();
}
return something;
}
private void closeDatabaseResources() {
try {
if (conn != null) {
System.out.println("conn closed");
conn.close();
}
if (st != null) {
System.out.println("st closed");
st.close();
}
if (rs != null) {
System.out.println("rs closed");
rs.close();
}
if (ps != null) {
System.out.println("ps closed");
ps.close();
}
} catch(SQLException se) {
se.printStackTrace();
}
}
}
Основываясь на ответе @erickson, почему бы просто не сделать это в одном блоке try, как это?
private void doEverythingInOneSillyMethod(String key) throws MyAppException
{
try (Connection db = ds.getConnection();
PreparedStatement ps = db.prepareStatement(...)) {
db.setReadOnly(true);
ps.setString(1, key);
ResultSet rs = ps.executeQuery()
...
} catch (SQLException ex) {
throw new MyAppException("Query failed.", ex);
}
}
Обратите внимание, что вам не нужно создавать объект ResultSet внутри блока try, поскольку объекты ResultSet автоматически закрываются при закрытии объекта PreparedStatement.
A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.
Ссылка: https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html
Это решение работает очень хорошо, если вы используете свое собственное, потому что исключения SQLExceptions, создаваемые методами close, в любом случае не подлежат восстановлению. Еще один изящный способ сделать это - выбросить из этого метода подкласс RuntimeException, который переходит на уровень кода, который может управлять сбоями БД.