Как правильно закрыть ресурсы - PullRequest
18 голосов
/ 26 апреля 2011

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

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

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

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

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

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

Но как насчет других возможных исключений?Согласно спецификации API Java, Statement.close () может генерировать исключение SQLException «в случае ошибки базы данных».Поэтому, даже если мой CallableStatement не равен NULL и я могу успешно вызвать для него функцию 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 */ }

}

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

Ответы [ 6 ]

10 голосов
/ 26 апреля 2011

Я думаю, что лучший ответ уже упоминался, но я подумал, что было бы интересно упомянуть, что вы могли бы рассмотреть новую функцию автозапускаемых ресурсов 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 сейчас, но для тех, кто может начать играть с предварительным просмотром для разработчиков, это предлагает интересный способ ведения дел и, безусловно, может отказаться от других подходов в ближайшем будущем..

3 голосов
/ 14 декабря 2011

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

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

От docs.oracle.com:

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

Вызов close () для Соединения освобождает его базу данных и ресурсы JDBC.

3 голосов
/ 26 апреля 2011

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

@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);
}

и пусть Исключение всплывет или обработайте его, как хотите.

3 голосов
/ 26 апреля 2011

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

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);
}

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

2 голосов
/ 26 апреля 2011

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

0 голосов
/ 26 апреля 2011

Вы можете заключить один блок в другой:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...