Управление транзакциями, commit rollback

Транзакция Transaction включает одно или несколько изменений в базе данных, которые после выполнения либо все фиксируются (commit), либо все откатываются назад (rollback). При вызове метода commit или rollback текущая транзакция заканчивается и начинается другая.

По умолчанию каждое новое соединение находится в режиме автофиксации (autocommit = true). Это означает автоматическую фиксацию (commit) транзакции после выполнения каждого запроса. В этом случае транзакция включает только одно изменение (один запрос).

Если autocommit запрещен, т.е. равен false, то транзакция не заканчивается до явного вызова commit или rollback, включая, таким образом, все выражения, выполненные с момента последнего вызова commit или rollback. В этом случае все SQL-запросы в транзакции фиксируются или откатываются группой.

Метод фиксации commit завершает все изменения в БД, проделанные SQL-выражением, и снимает также все блокировки, установленные транзакцией. Метод rollback наоборот - не сохранит изменения и восстановит исходное состояние на момент начала транзакции.

Иногда пользователю нужно, чтобы какое-либо изменение не вступило в силу до тех пор, пока не вступит в силу предыдущее изменение. Этого можно достичь запрещением autocommit и группировкой обоих запросов в одну транзакцию. Если оба изменения произошли успешно, то вызывается метод commit, который переносит эффект от этих изменений в БД; если одно или оба запроса не прошли, то вызывается метод rollback, который возвращает прежнее состояние БД.

Большинство JDBC-драйверов поддерживают транзакции. В действительности драйвер, соответствующий спецификации JDBC, обязан поддерживать их. Интерфейс DatabaseMetaData позволяет получить информацию об уровнях изолированности транзакций, которые поддерживаются данной СУБД.

Пример использования транзакции - commit, autocommit

Connection connection = ...;
// Сброс автофиксации
connection.setAutoCommit(false);
// Первая транзакция
PreparedStatement updateSales = connection.prepareStatement(
         "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
updateSales.setInt(1, 50);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();

// Вторая транзакция
PreparedStatement updateTotal = connection.prepareStatement(
        "UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE ?");
updateTotal.setInt(1, 50);
updateTotal.setString(2, "Colombian");
updateTotal.executeUpdate();
// Завершение транзакции
connection.commit();
// Восстановление по умолчанию
connection.setAutoCommit(true);

В примере для соединения Connection режим автофиксации отключен и два оператора updateSales и updateTotal будут зафиксированы вместе при вызове метода commit.

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

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

Уровни изолированности транзакций, dirty read

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

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

Есть несколько способов разрешения конфликтов между одновременно выполняющимися транзакциями. Разработчик может определить уровень изолированности так, что пока одна транзакция изменяет какое-либо значение, вторая транзакция могла бы прочитать обновленное значение до того, пока первая не выполнит commit или rollback. Для этого следует установить уровень изолированности TRANSACTION_READ_UNCOMMITTED:

Connection connection;
... 
connection.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);

В данном коде серверу указано на возможность чтения измененных значений до того, как выполнится commit, т.е. определена возможность "грязного чтения" ("dirty read").

По умолчанию уровень изоляции транзакций обычно установлен в READ_COMMITED.

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

В связи с тем, что уровни изоляции, предлагаемые различными поставщиками СУБД, могут меняться, Вам следует обратиться к документации за дополнительной информацией. Уровни изоляции не стандартизованы для платформы J2EE.

Чем выше уровень изолированности транзакций, тем больше внимания СУБД уделяет устранению конфликтов. Интерфейс Connection определяет пять таких уровней. Минимальный из них соответствует случаю, когда транзакции не поддерживаются вовсе, а максимальный - невозможности существования более одной транзакции в любой момент времени.

Обычно, чем выше уровень изолированности, тем медленнее выполняется приложение (из-за избыточной блокировки). При выборе конкретного уровня изолированности разработчик должен найти золотую середину между потребностями в производительности и требованиями к целостности данных. Очевидно, что реально поддерживаемые уровни зависят от возможностей используемой СУБД.

При создании объекта Connection уровень его изолированности зависит от драйвера или БД. Можно вызвать метод setIsolationLevel, чтобы изменить уровень изолированности транзакций, и новое значение уровня будет установлено до конца сессии. Чтобы установить уровень изолированности только для одной транзакции, надо установить его перед выполнением транзакции и восстановить прежнее значение после ее завершения.

Типы уровней изолированности

  • TRANSACTION_NONE
    Транзакции не поддерживаются.
  • TRANSACTION_READ_COMMITTED
    Запрет на «грязное чтение» (dirty read). Данный уровень блокирует транзакциям чтение строк с неподтвержденными изменениями в них.
  • TRANSACTION_READ_UNCOMMITTED
    Разрешение на «dirty read». Данный уровень позволяет изменять строку с помощью одной транзакции и прочесть ее другой прежде, чем изменения в этой строке будут подтверждены (dirty read). Если изменения будут отменены с помощью rollback(), вторая транзакция вернет неправильную строку.
  • TRANSACTION_REPEATABLE_READ
    Запрет на «dirty read». Данный уровень препятствует транзакции от чтения строки с неподтвержденным изменением в ней, он также предотвращает ситуацию, когда одна транзакция читает строку, а вторая транзакция изменяет ее, при этом первая транзакция перечитывая строку, получает разные значения каждый раз (разовое чтение).
  • TRANSACTION_SERIALIZABLE
    Запрет на «dirty read». Данный уровень включает предотвращения из TRANSACTION_REPEATABLE_READ, более того предотвращает ситуацию, когда одна транзакция читает все строки, которые удовлетворяют условию WHERE, а вторая транзакция вставляет строку, которая удовлетворяет тому же условию WHERE, и первая транзакция, перечитывая с тем же условием, получает дополнительную «фантомную» строку при втором чтении.
  Рейтинг@Mail.ru