Где закрыть java PreparedStatements и результирующие наборы?

StackOverflow https://stackoverflow.com/questions/321418

  •  11-07-2019
  •  | 
  •  

Вопрос

Рассмотрим код:

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?Или переместить инструкции close в предложение try?Или просто не утруждать себя звонком близким?

Это было полезно?

Решение

Для файлового ввода-вывода я обычно добавляю try / catch в блок finally. Однако вы должны быть осторожны, чтобы не создавать исключений из блока finally, так как они приведут к потере исходного исключения (если оно есть).

В этой статье приведен более конкретный пример закрытия соединения с базой данных.

Другие советы

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

Если вы действительно катаете свой собственный jdbc, он определенно запутается. В конце концов, close () в завершение должен быть заключен в собственный метод try, который, по крайней мере, ужасен. Вы не можете пропустить закрытие, хотя ресурсы будут очищены при закрытии соединения (что может быть не сразу, если вы используете пул). На самом деле, одним из главных преимуществ использования фреймворка (например, hibernate) для управления доступом к БД является управление соединением и обработкой набора результатов, чтобы вы не забыли закрыть.

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

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

Не тратьте свое время на программирование низкоуровневого управления исключениями, используйте высокоуровневый API, такой как Spring-JDBC, или пользовательскую оболочку вокруг объектов connection / Statement / rs, чтобы скрыть беспорядочный код, обработанный при попытке. / р>

Также обратите внимание:

" Когда объект Statement закрыт, его текущий объект ResultSet, если таковой существует, также закрывается. & Quot;

http: // java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close ()

Должно быть достаточно закрыть только PreparedStatement в finally, и только если оно еще не закрыто. Если вы хотите быть очень точным, закройте ResultSet FIRST, а не после закрытия PreparedStatement (закрытие его после, как в некоторых примерах здесь, должно фактически гарантировать исключение, так как оно уже закрыто).

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

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

Обратите внимание, что ваше текущее решение не закроет ResultSet, если не удастся закрыть PreparedStatement - лучше использовать вложенные блоки finally.

Если вы используете Java 7, вы можете использовать улучшения в механизмах обработки исключений в тех классах, которые реализуют AutoCloseable (т. Е. PreparedStatement , Resultset )

Вам также может быть интересен этот вопрос: Закрытие ResultSet в Java 7

Я знаю, что это старый вопрос, но на всякий случай, если кто-то ищет ответ, теперь у java есть решение попробовать с ресурсом.

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

Не опускайте вызов близко. Это может вызвать проблемы.

Я предпочитаю добавлять блок try / catch в окончание.

Вероятно, старый (хотя и простой) способ сделать что-то, но он все еще работает:

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 объект закрыт.

  

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

Ссылка: https://docs.oracle .com / JavaSE / 7 / документы / API / Java / SQL / ResultSet.html

сосредоточьтесь, наконец, на предложении,

finally {
   try {
      rs.close();
      ps.close();
   } catch (Exception e) {
      // Do something
   }
}

Я думаю, вам нужно изменить 2 пункта.

Во-первых, снова используйте try & catch в предложении fainlly .

Во-вторых, выполните rs.close(), прежде чем выполнять ps.close().

fly1997@naver.com

Я использую это ..

finally
{
    if (ps != null) ps.close();
    if (rs != null) rs.close();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top