След: JDBC (ТМ) Доступ к базе данных
Урок: Основы JDBC
Используя Транзакции
Домашняя страница > JDBC (ТМ) Доступ к базе данных > Основы JDBC

Используя Транзакции

Есть времена, когда Вы не хотите, чтобы один оператор вступил в силу, если другой не завершается. Например, когда владелец Перерыва на кофе обновляет количество кофе, продаваемого каждую неделю, владелец будет также хотеть обновить общую сумму, проданную до настоящего времени. Однако, количество, проданное в неделю и проданную общую сумму, должно быть обновлено одновременно; иначе, данные будут непоследовательны. Способ убедиться, что или оба действия происходят или никакое действие, происходит, должен использовать транзакцию. Транзакция является рядом той или большего количества операторов, который выполняется как модуль, таким образом, или все операторы выполняются, или ни один из операторов не выполняется.

Эта страница затрагивает следующие темы

Отключение Режима Автоматической фиксации

Когда соединение создается, это находится в режиме автоматической фиксации. Это означает, что каждый отдельный SQL-оператор обрабатывается как транзакция и автоматически фиксируется прямо после того, как он выполняется. (Чтобы быть более точным, значение по умолчанию для SQL-оператора, который будет фиксироваться, когда оно завершается, не, когда оно выполняется. Оператор завершается, когда все его наборы результатов и количества обновления были получены. В почти всех случаях, однако, оператор завершается, и поэтому фиксируется, прямо после того, как он выполняется.)

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

con.setAutoCommit(false);

Фиксация Транзакций

После того, как режим автоматической фиксации отключается, никакие SQL-операторы не фиксируются, пока Вы не вызываете метод commit явно. Все операторы, выполняемые после предыдущего звонка в метод commit включаются в текущую транзакцию и фиксируются вместе как модуль. Следующий метод, CoffeesTable.updateCoffeeSales, в котором con активное соединение, иллюстрирует транзакцию:

public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
    throws SQLException {

    PreparedStatement updateSales = null;
    PreparedStatement updateTotal = null;

    String updateString =
        "update " + dbName + ".COFFEES " +
        "set SALES = ? where COF_NAME = ?";

    String updateStatement =
        "update " + dbName + ".COFFEES " +
        "set TOTAL = TOTAL + ? " +
        "where COF_NAME = ?";

    try {
        con.setAutoCommit(false);
        updateSales = con.prepareStatement(updateString);
        updateTotal = con.prepareStatement(updateStatement);

        for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
            updateSales.setInt(1, e.getValue().intValue());
            updateSales.setString(2, e.getKey());
            updateSales.executeUpdate();
            updateTotal.setInt(1, e.getValue().intValue());
            updateTotal.setString(2, e.getKey());
            updateTotal.executeUpdate();
            con.commit();
        }
    } catch (SQLException e ) {
        JDBCTutorialUtilities.printSQLException(e);
        if (con != null) {
            try {
                System.err.print("Transaction is being rolled back");
                con.rollback();
            } catch(SQLException excep) {
                JDBCTutorialUtilities.printSQLException(excep);
            }
        }
    } finally {
        if (updateSales != null) {
            updateSales.close();
        }
        if (updateTotal != null) {
            updateTotal.close();
        }
        con.setAutoCommit(true);
    }
}

В этом методе режим автоматической фиксации отключается для соединения con, что означает что два готовых оператора updateSales и updateTotal фиксируются вместе когда метод commit вызывается. Всякий раз, когда commit метод вызывают (или автоматически когда режим автоматической фиксации включается или явно когда это отключается), все изменения, следующие из операторов в транзакции, делаются постоянными. В этом случае это означает что SALES и TOTAL столбцы для колумбийского кофе были изменены на 50 (если TOTAL был 0 ранее), и сохранит это значение, пока они не изменяются с другим оператором обновления.

Оператор con.setAutoCommit(true); включает режиму автоматической фиксации, что означает, что каждый оператор еще раз фиксируется автоматически, когда это завершается. Затем, Вы вернулись к состоянию по умолчанию, где Вы не должны вызвать метод commit самостоятельно. Желательно отключить режим автоматической фиксации только во время режима транзакции. Таким образом, Вы избегаете содержать блокировки базы данных для многократных операторов, который увеличивает вероятность конфликтов с другими пользователями.

Используя Транзакции, чтобы Сохранить Целостность Данных

В дополнение к собиранию в группу операторов для выполнения как модуль транзакции могут помочь сохранить целостность данных в таблице. Например, предположите, что сотрудник, как предполагалось, ввел новые цены на кофе в таблицу COFFEES но задержанное выполнение этого в течение нескольких дней. Тем временем цены выросли, и сегодня владелец находится в процессе ввода более высоких цен. Сотрудник наконец находит время для ввода теперь устаревших цен в то же самое время, когда владелец пытается обновить таблицу. После вставки устаревших цен сотрудник понимает, что они больше не действительны, и вызывает Connection метод rollback отменить их эффекты. (Метод rollback прерывает транзакцию и восстанавливает значения к тому, чем они были перед предпринятым обновлением.) Одновременно, владелец выполняет a SELECT оператор и печать новых цен. В этой ситуации возможно, что владелец напечатает цену, которая откатывалась к ее предыдущему значению, делая печатную неправильную цену.

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

Чтобы избежать конфликтов во время транзакции, DBMS использует блокировки, механизмы для того, чтобы блокировать доступ другими к данным, к которым получает доступ транзакция. (Отметьте, что в режиме автоматической фиксации, где каждый оператор является транзакцией, блокировки сохранены только для одного оператора.) После того, как блокировка устанавливается, она остается в силе, пока транзакция не фиксируется или откатывается. Например, DBMS мог заблокировать строку таблицы, пока обновления к нему не фиксировались. Эффект этой блокировки состоял бы в том, чтобы препятствовать тому, чтобы пользователь получил грязное чтение, то есть, читая значение прежде, чем это будет сделано постоянным. (Доступ к обновленному значению, которое не фиксировалось, считают грязным чтением, потому что для того значения возможно откатываться к его предыдущему значению. Если Вы читаете значение, которое позже откатывается, Вы считаете недопустимое значение.)

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

Один пример уровня изоляции транзакции TRANSACTION_READ_COMMITTED, который не будет позволять значению быть полученным доступ, пока оно не фиксировалось. Другими словами, если уровень изоляции транзакции устанавливается в TRANSACTION_READ_COMMITTED, DBMS не позволяет грязным чтениям происходить. Интерфейс Connection включает пять значений, которые представляют уровни изоляции транзакции, которые можно использовать в JDBC:

Уровень изоляции Транзакции Грязные чтения Неповторяемые чтения Фантомные Чтения
TRANSACTION_NONE Не поддерживаемый Не применимый Не применимый Не применимый
TRANSACTION_READ_COMMITTED Поддерживаемый Предотвращенный Позволенный Позволенный
TRANSACTION_READ_UNCOMMITTED Поддерживаемый Позволенный Позволенный Позволенный
TRANSACTION_REPEATABLE_READ Поддерживаемый Предотвращенный Предотвращенный Позволенный
TRANSACTION_SERIALIZABLE Поддерживаемый Предотвращенный Предотвращенный Предотвращенный

Неповторяемое чтение происходит, когда транзакция A получает строку, транзакция B впоследствии обновляет строку, и транзакцию, более позднее получает ту же самую строку снова. Транзакция A получает ту же самую строку дважды, но видит различные данные.

Фантомное чтение происходит, когда транзакция A получает ряд строк, удовлетворяющих данное условие, транзакция B впоследствии вставляет или обновляет строку так, что, строка теперь удовлетворяет условию в транзакции A, и транзакции более поздние повторения условное извлечение. Транзакция теперь видит дополнительную строку. Эта строка упоминается как фантом.

Обычно, Вы ничего не должны сделать об уровне изоляции транзакции; можно только использовать значение по умолчанию один для Вашего DBMS. Уровень изоляции транзакции значения по умолчанию зависит от Вашего DBMS. Например, для DB Java, это TRANSACTION_READ_COMMITTED. JDBC позволяет Вам узнавать, в какой уровень изоляции транзакции Ваш DBMS устанавливается (использование Connection метод getTransactionIsolation) и также позволяет Вам устанавливать это в другой уровень (использующий Connection метод setTransactionIsolation).

Отметьте: драйвер JDBC не мог бы поддерживать все уровни изоляции транзакции. Если драйвер не поддерживает уровень изоляции, определенный в вызове setTransactionIsolation, драйвер может заменить более высоким, более рестриктивным уровнем изоляции транзакции. Если драйвер не может заменить более высоким уровнем транзакции, он бросает a SQLException. Используйте метод DatabaseMetaData.supportsTransactionIsolationLevel определить, поддерживает ли драйвер данный уровень.

Установка и Откат к Точкам сохранения

Метод Connection.setSavepoint, наборы a Savepoint объект в пределах текущей транзакции. Connection.rollback метод перегружается, чтобы взять a Savepoint параметр.

Следующий метод, CoffeesTable.modifyPricesByPercentage, повышает цену определенного кофе на процент, priceModifier. Однако, если новая цена больше чем указанная цена, maximumPrice, тогда цена возвращается к исходной цене:

public void modifyPricesByPercentage(
    String coffeeName,
    float priceModifier,
    float maximumPrice)
    throws SQLException {
    
    con.setAutoCommit(false);

    Statement getPrice = null;
    Statement updatePrice = null;
    ResultSet rs = null;
    String query =
        "SELECT COF_NAME, PRICE FROM COFFEES " +
        "WHERE COF_NAME = '" + coffeeName + "'";

    try {
        Savepoint save1 = con.setSavepoint();
        getPrice = con.createStatement(
                       ResultSet.TYPE_SCROLL_INSENSITIVE,
                       ResultSet.CONCUR_READ_ONLY);
        updatePrice = con.createStatement();

        if (!getPrice.execute(query)) {
            System.out.println(
                "Could not find entry " +
                "for coffee named " +
                coffeeName);
        } else {
            rs = getPrice.getResultSet();
            rs.first();
            float oldPrice = rs.getFloat("PRICE");
            float newPrice = oldPrice + (oldPrice * priceModifier);
            System.out.println(
                "Old price of " + coffeeName +
                " is " + oldPrice);

            System.out.println(
                "New price of " + coffeeName +
                " is " + newPrice);

            System.out.println(
                "Performing update...");

            updatePrice.executeUpdate(
                "UPDATE COFFEES SET PRICE = " +
                newPrice +
                " WHERE COF_NAME = '" +
                coffeeName + "'");

            System.out.println(
                "\nCOFFEES table after " +
                "update:");

            CoffeesTable.viewTable(con);

            if (newPrice > maximumPrice) {
                System.out.println(
                    "\nThe new price, " +
                    newPrice +
                    ", is greater than the " +
                    "maximum price, " +
                    maximumPrice +
                    ". Rolling back the " +
                    "transaction...");

                con.rollback(save1);

                System.out.println(
                    "\nCOFFEES table " +
                    "after rollback:");

                CoffeesTable.viewTable(con);
            }
            con.commit();
        }
    } catch (SQLException e) {
        JDBCTutorialUtilities.printSQLException(e);
    } finally {
        if (getPrice != null) { getPrice.close(); }
        if (updatePrice != null) {
            updatePrice.close();
        }
        con.setAutoCommit(true);
    }
}

Следующий оператор определяет что курсор ResultSet объект, сгенерированный от getPrice запрос закрывается когда commit метод вызывают. Отметьте это, если Ваш DBMs не поддерживает ResultSet.CLOSE_CURSORS_AT_COMMIT, тогда эта константа игнорируется:

getPrice = con.prepareStatement(query, ResultSet.CLOSE_CURSORS_AT_COMMIT);

Метод начинается, создавая a Savepoint со следующим оператором:

Savepoint save1 = con.setSavepoint();

Метод проверяет, больше ли новая цена чем maximumPrice значение. Если так, метод откатывает транзакцию со следующим оператором:

con.rollback(save1);

Следовательно, когда метод фиксирует транзакцию, вызывая Connection.commit метод, это не будет фиксировать строк чей связанный Savepoint откатывался; это будет фиксировать все другие обновленные строки.

Выпуск Точек сохранения

Метод Connection.releaseSavepoint берет a Savepoint возразите в качестве параметра, и удаляет это из текущей транзакции.

После того, как точка сохранения была выпущена, пытаясь сослаться на нее в работе отката вызывает a SQLException быть брошенным. Любые точки сохранения, которые были созданы в транзакции, автоматически выпускаются и становятся недопустимыми, когда транзакция фиксируется, или когда вся транзакция откатывается. Откат транзакции к точке сохранения автоматически выпускает и делает недопустимым любые другие точки сохранения, которые создавались после рассматриваемой точки сохранения.

Когда Вызвать откат Метода

Как отмечалось ранее, вызов метода rollback завершает транзакцию и возвращает любые значения, которые были изменены к их предыдущим значениям. Если Вы пытаетесь выполнить один или более операторов в транзакции и получить a SQLException, вызовите метод rollback закончить транзакцию и запустить транзакцию снова и снова. Это - единственный способ знать то, что фиксировалось и что не фиксировалось. Ловля a SQLException говорит Вам, что что-то является неправильным, но это не говорит Вам, что было или не фиксировалось. Поскольку невозможно рассчитывать на факт, что ничто не фиксировалось, вызывая метод rollback единственный способ быть уверенным.

Метод CoffeesTable.updateCoffeeSales демонстрирует транзакцию и включает a catch блок, который вызывает метод rollback. Если приложение продолжает и использует результаты транзакции, этот звонок rollback метод в catch блок предотвращает использование возможно неправильных данных.


Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Используя Готовые Операторы
Следующая страница: Используя Объекты RowSet



Spec-Zone.ru - all specs in one place