Вопросы о правильном закрытии / фиксации транзакций базы данных и выполнении запросов - PullRequest
4 голосов
/ 02 ноября 2011

Я работаю над обновлением проекта, и я просто хочу узнать лучшие практики по этим двум вопросам:

  1. Должен ли я продолжать использовать Connections / Statements / ResultSet для выполнения запросов или я должен использовать что-то еще?
  2. Это правильный способ фиксации / закрытия соединений / операторов / наборов результатов? Я имею в виду, делаю ли я коммиты / закрытия в правильном порядке с блоками try / catch / finally в нужных местах?

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

Фрагмент кода из метода select:

public  ArrayList getMethod() {

    ArrayList a = new ArrayList();

    Connection con = null;
    ResultSet rs = null;
    Statement stmt = null;

    try {

        con = BoneCPConnection.getConnectionPool().getConnection();     // get a connection
        con.setAutoCommit(true);            
        stmt = con.createStatement();

        String query = "select * from example";

        rs = stmt.executeQuery(query);

        while(rs.next()) {
            System.out.println("Result: "+ rs.getInt(1));
        }

    } catch (Exception e) {
        System.out.println("Issue with getMethod");
        e.printStackTrace();
    } finally {

        try {
            rs.close();
            stmt.close();
            con.close();
        } catch (Exception e) {
            con = null;
        }

        rs = null;
        stmt = null;
        con = null;
    }

    return a;
}

Фрагмент кода из метода update type

public void updateMethod() {

    ArrayList a = new ArrayList();

    Connection con = null;
    Statement stmt = null;
    int updateCount = null;

    try {

        con = BoneCPConnection.getConnectionPool().getConnection();     // get a connection     
        stmt = con.createStatement();

        String query = "update example set id = 1";

        updateCount = stmt.executeUpdate(query);

        System.out.println("Result: "+ updateCount);    

    } catch (Exception e) {
        System.out.println("Issue with updateMethod");
        e.printStackTrace();
    } finally {

        try {
            con.commit();
            stmt.close();
            con.close();
        } catch (Exception e) {
            con = null;
        }

        stmt = null;
        con = null;
    }
}

Ответы [ 4 ]

7 голосов
/ 23 ноября 2011

Как минимум, вам, вероятно, следует переключиться на PreparedStatement вместо простых утверждений.Причина этого заключается в том, что драйвер JDBC в большинстве случаев отправляет инструкцию в базу данных при создании, чтобы ее можно было предварительно скомпилировать.Затем вы можете связать свои параметры с оператором и выполнить.Помимо преимуществ производительности при прекомпиляции, вы также получаете небольшую защиту от атак с использованием SQL-инъекций, поскольку способ задания параметров более строго типизирован.На сайте Oracle есть хорошее описание подготовленных операторов.

Если вы используете Spring (или хотите сделать скачок, чтобы добавить его в свою систему), вы можете посмотретьв классах JdbcTemplate и JdbcDaoSupport (оба обозначены здесь ).Основным преимуществом является то, что он заботится о коде очистки соединения для вас (так что вам не нужно сильно беспокоиться о пропущенном вызове close).

Аналогичным образом, если вы добавите Spring в свой проект, вы можете использовать его для настройки транзакций (с помощью аннотаций или из файла контекста Spring).Это позволит вам вывести управление транзакциями из реальной реализации и сделать код в вашем Dao немного чище.

Что касается обработки commit / close: вы должны убрать свои операторы commit из вашего finallyблоки и в основной путь выполнения.Вы должны хранить свои операторы close в блоке finally, хотя, поскольку вы хотите следовать, они происходят независимо от того, что.

Пример того, как будет выглядеть ваш код обновления с использованием PreparedStatements:

public void updateMethod() {
    Connection con = null;
    PreparedStatement stmt = null;
    int updateCount = null;

    try {
        con = BoneCPConnection.getConnectionPool().getConnection();
        stmt = con.prepareStatement("update example set id = ?");        
        stmt.setInt(1,1);
        updateCount = stmt.executeUpdate(query);
        con.commit();
    } catch (Exception e) {
       if(con != null){
        con.rollback();
       }
    } finally {

        try {
          if(stmt != null){
            stmt.close();
          }
          if(con != null){                
            con.close();
          }
        } catch (Exception e) {
            con = null;
        }        
    }
}

Если вы используете Spring JdbcDaoSuport, он будет выглядеть так:

public class YourDao extends JdbcDaoSupport{

  public void updateMethod(){
    String sql = "update example set id = ?";
    getJdbcTemplate().update(sql, new Object[] { new Integer(1)});           
  }

}
4 голосов
/ 23 ноября 2011
  1. Вы почти всегда должны предпочитать PreparedStatement для параметризованных запросов, как это:

    • защищает вас от инъекций sql

    • прекомпилируется базой данных

  2. Вам не нужно повторно инициализировать rs, stmt и con в null, когда они выходят из области видимости в конце метода

  3. Вам почти всегда лучше использовать некоторую структуру доступа к БД. Одним из примеров является Spring с JdbcTemplate, другим является Apache Commons DBUtils

  4. Если вы обновляете систему, обновите ее до java 7 - вы получите try-with-resources, который закроет ваши соединения и операторы бесплатно, и ваш код будет выглядеть так:



    public void updateMethod() {
        int updateCount = 0;

        String query = "update example set id = ?";
        try (Connection con = BoneCPConnection.getConnectionPool().getConnection();                         
            PreparedStatement stmt = con.prepareStatement(query)) {
                stmt.setInt(1, 1);

                updateCount = stmt.executeUpdate();

                System.out.println("Result: " + updateCount);

        } catch (Exception e) {
            System.out.println("Issue with updateMethod");
            e.printStackTrace();
        }
    }

    public ArrayList getMethod() {

        ArrayList a = new ArrayList();
        String query = "select * from example";

        try (Connection con = BoneCPConnection.getConnectionPool().getConnection(); 
            Statement stmt = con.createStatement()) {

            con.setAutoCommit(false);

            try (
                ResultSet rs = stmt.executeQuery(query)) {

                while (rs.next()) {
                    System.out.println("Result: " + rs.getInt(1));
                }

            }

        } catch (Exception e) {
            System.out.println("Issue with getMethod");
            e.printStackTrace();
        }

        return a;

    }
3 голосов
/ 02 ноября 2011

самая важная вещь, которую нужно закрыть - это соединение.как правило, вы хотите попробовать / наконец каждый ресурс (rs, stmt, conn) при закрытии.

также вы не хотите фиксировать в блоке finally.вы только хотите зафиксировать по счастливому пути (внутри блока catch).

наконец, никогда, никогда, никогда вставьте "e.printStackTrace ()" в ваш код.

ОБНОВЛЕНИЕ:

Пояснение к предыдущему предупреждению о printStackTrace(): Это запах кода, и это, скорее всего, означает, что программа проглатывает исключение.он автоматически вставляется IDE или вручную программистами и означает, что они на самом деле не обрабатывают исключение.однажды, где-нибудь, они захотят увидеть это исключение, но оно будет потеряно навсегда.(тогда есть наихудший сценарий, когда код случайно продолжается, как будто исключение не происходит, оставляя код в потопленном состоянии).Существует два основных варианта:

  • , если вы хотите продолжить после исключения, запишите его в надлежащее средство ведения журнала (используйте java.util.Logger, если ничего больше)
  • , если выне может обработать исключение, повторно выбросить его

(технически, есть также немногие и далеко случай, когда это действительно игнорируетсяисключение, в этом случае комментарий добавляется как // i never, ever care if i get this exception).

2 голосов
/ 23 ноября 2011

Ответ на вопрос 1: - если вы не увлекаетесь фреймворками и бизнес-логика взаимодействия с БД достаточно проста, вы можете продолжить использовать этот подход. Другие альтернативы: Hibernate, JPA или Spring JDBC. Все они делают то же самое, похожее на ваш код, но скрыто в реализации фреймворка. Каждая из этих структур имеет свои преимущества / недостатки. Но опять же, это вопрос предпочтений / требований / сложности.

Ответ на вопрос 2: - вы открываете / закрываете ресурсы в правильном порядке, однако есть некоторые проблемы: 1. Попробуйте поймать более конкретное исключение, а не исключение. SQLException должно быть поймано в вашем примере. 2. Лучше использовать PreparedStatement вместо Statement, чтобы избежать внедрения SQL или если вы выполняете один и тот же запрос несколько раз. 3. Производственный код не должен использовать System.out.println или e.printStackTrace, но для всплесков или отработки новых вещей это не проблема. (Что касается предыдущего ответа).

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