Подтвердить что ты не робот

Как правильно закрыть ресурсы

Когда я очищал какой-то код, FindBugs указывал мне на код JDBC, который использует объекты Connection, CallableStatement и ResultSet. Вот фрагмент из этого кода:

CallableStatement cStmt = getConnection().prepareCall("...");
...
ResultSet rs = cStmt.executeQuery();

while ( rs.next() )
{
    ...
}

cStmt.close();
rs.close();
con.close();

FindBugs отметил, что они должны быть в конечном блоке. Я начал рефакторинг моего кода, чтобы сделать это, и я начал задаваться вопросом, как обрабатывать код в блоке finally.

Возможно, что создание объектов CallableStatement of Connection вызовет исключение, оставив объект ResultSet равным нулю. Когда я пытаюсь закрыть ResultSet, я получаю исключение NullPointerException, и мое соединение, в свою очередь, никогда не будет закрыто. В самом деле, этот поток раскрывает ту же концепцию и показывает, что упаковка ваших вызовов close() в нулевой проверке является хорошей идеей.

Но как насчет других возможных исключений? Согласно спецификации Java API, Statement.close() может вызывать SQLException "при возникновении ошибки базы данных". Таким образом, даже если мой CallableStatement не является нулевым, и я могу успешно вызвать close() на нем, я все равно могу получить исключение и не иметь возможности закрыть другие ресурсы.

Единственное "безотказное" решение, о котором я могу думать, - это обернуть каждый вызов close() в свой собственный блок try/catch, например:

finally {

    try {
        cStmt.close();
    } catch (Exception e) { /* Intentionally Swallow  Exception */ }

    try {
        rs.close();
    } catch (Exception e) { /* Intentionally Swallow  Exception */ }

    try {
        con.close();
    } catch (Exception e) { /* Intentionally Swallow  Exception */ }

}
Мальчик, если это не выглядит ужасно. Есть ли лучший способ сделать это?
4b9b3361

Ответ 1

Я думаю, что лучший ответ уже упоминается, но я подумал, что может быть интересно упомянуть, что вы могли бы рассмотреть новую функцию JDK 7 для автозапускаемых ресурсов.

try{
    try(Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/hrdb", "obiwan", "kenobi"); 
        Statement stm = conn.createStatement(); 
        ResultSet rs = stm.executeQuery("select name from department")) {

        while(rs.next()){
            System.out.println(rs.getString("name"));
        }

    } 
}catch(SQLException e){
    //you might wanna check e.getSuppressed() as well
    //log, wrap, rethrow as desired.
}

Не все из нас могут перейти на JDK 7 сейчас, но для тех, кто может начать играть с предварительным просмотром разработчика, это предлагает интересный способ сделать что-то и, конечно же, может отказаться от других подходов в ближайшем будущем.

Ответ 2

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

public static void close(ResultSet rs) {
   try { if (rs != null) rs.close();
   } catch (SQLException (e) {
      log.error("",e);
   } 

}

Затем вы просто статируете импорт этого класса.

ваш, наконец, станет чем-то вроде

finally {
     close(resultset);
     close(statement);
     close(connection);
}

что действительно не так ужасно.

Ответ 3

Используйте Очистка Ломбока, если вы можете:

@Cleanup
Connection c = ...
@Cleanup
statement = c.prepareStatement(...);
@Cleanup
rs = statement.execute(...);

Это работает для трех вложенных блоков try-finally и отлично работает с исключением. Никогда не проглатывайте исключение без уважительной причины!

Альтернатива:

Напишите собственный способ утилиты:

public static void close(ResultSet rs, Statement stmt, Connection con) throws SQLException {
    try {
        try {
            if (rs!=null) rs.close();
        } finally {
            if (stmt!=null) stmt.close();
        }
    } finally {
        if (con!=null) con.close();
    }
}

и использовать его в

try {
    Connection con = ...
    Statement stmt = ...
    ResultSet rs = ...
} finally {
    close(rs, stmt, con);
}

и пусть Exception пузырится или обрабатывает его, как вы хотите.

Ответ 4

Вам нужно закрыть соединение.

try
{
    cStmt.close();
}
catch(Exception e)
{
    /* Intentionally Swallow Exception */
} 

От docs.oracle.com:

Объект Statement автоматически закрывается при сборе мусора. Когда объект Statement закрыт, его текущий объект ResultSet, если он существует, также закрыт.

Вызов функции close() в Connection освобождает базу данных и ресурсы JDBC.

Ответ 5

Единственный способ, которым я знаю, скрыть весь этот уродливый шаблон шаблона try-catch, - это использовать что-то вроде Spring JBDC Template.

Ответ 6

Вы можете перенести один блок в другой блок:

try{
  Connection c = ...
    try{
      statement = c.prepareStatement(...);
      try{
        rs = statement.execute(...);
      }finally{
        rs.close();
      }
    }finally{
      statement.close()
    }
  }finally{
    c.close();
  }
}catch(SQLException e){}

Используйте самый нижний блок catch для всего, что может возникнуть