32 урока по Delphi

Урок 24: Управление транзакциями

Содержание урока 24:

Обзор
SQL-выражения для управления транзакциями
Запуск транзакции
Завершение транзакции
Управление транзакциями в Delphi

1. Обзор

Все операции, выполняемые с данными на SQL сервере, происходят в контексте транзакций. Транзакция - это групповая операция, т.е. набор действий с базой данных; самым существенным для этих действий является правило либо все, либо ни чего. Если во время выполнения данного набора действий, на каком-то этапе невозможно произвести очередное действие, то нужно выполнить возврат базы данных к начальному состоянию (произвести откат транзакции). Таким образом (при правильном планировании транзакций), обеспечивается целостность базы данных. В данном уроке объясняется, как начинать, управлять и завершать транзакции с помощью SQL выражений. А так же рассматривается вопрос об использовании транзакций в приложениях, созданных в Delphi. Вся приведенная информация касается InterBase.

2. SQL-выражения для управления транзакциями

Для управления транзакциями имеется три выражения:

SET TRANSACTION - Начинает транзакцию и определяет ее поведение.

COMMIT - Сохраняет изменения, внесенные транзакцией, в базе данных и завершает транзакцию.

ROLLBACK - Отменяет изменения, внесенные транзакцией, и завершает транзакцию.

3. Запуск транзакции

Выполнять транзакции можно, например, из Windows Interactive SQL, из программы, из сохраненной процедуры или триггера. В общем виде, синтаксис команды SQL для запуска транзакции:

SET TRANSACTION [Access mode] [Lock Resolution]

[Isolation Level] [Table Reservation]

Значения, принимаемые по-умолчанию:

выражение

SET TRANSACTION

равносильно выражению

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT

Access Mode - определяет тип доступа к данным. Может принимать два значения:

- READ ONLY - указывает, что транзакция может только читать данные и не может модифицировать их.
- READ WRITE - указывает, что транзакция может читать и модифицировать данные. Это значение принимается по умолчанию.

Пример:

SET TRANSACTION READ WRITE

Isolation Level - определяет порядок взаимодействия данной транзакции с другими в данной базе. Может принимать значения:

- SNAPSHOT - значение по умолчанию. Внутри транзакции будут доступны данные в том состоянии, в котором они находились на момент начала транзакции. Если по ходу дела в базе данных появились изменения, внесенные другими завершенными транзакциями, то данная транзакция их не увидит. При попытке модифицировать такие записи возникнет сообщение о конфликте.
- SNAPSHOT TABLE STABILITY - предоставляет транзакции исключительный доступ к таблицам, которые она использует. Другие транзакции смогут только читать данные из них.
- READ COMMITTED - позволяет транзакции видеть текущее состояние базы.

Конфликты, связанные с блокировкой записей происходят в двух случаях:

- Транзакция пытается модифицировать запись, которая была изменена или удалена уже после ее старта. Транзакция типа READ COMMITTED может вносить изменения в записи, модифицированные другими транзакциями после их завершения.
- Транзакция пытается модифицировать таблицу, которая заблокирована другой транзакцией типа SNAPSHOT TABLE STABILITY.

Lock Resolution - определяет ход событий при обнаружении конфликта блокировки. Может принимать два значения:

- WAIT - значение по умолчанию. Ожидает разблокировки требуемой записи. После этого пытается продолжить работу.
- NO WAIT - немедленно возвращает ошибку блокировки записи.

Table Reservation - позволяет транзакции получить гарантированный доступ необходимого уровня к указанным таблицам. Существует четыре уровня доступа:

- PROTECTED READ - запрещает обновление таблицы другими транзакциями, но позволяет им выбирать данные из таблицы.
- PROTECTED WRITE - запрещает обновление таблицы другими транзакциями, читать данные из таблицы могут только транзакции типа SNAPSHOT или READ COMMITTED.
- SHARED READ - самый либеральный уровень. Читать могут все, модифицировать - транзакции READ WRITE.
- SHARED WRITE - транзакции SNAPSHOT или READ COMMITTED READ WRITE могут модифицировать таблицу, остальные - только выбирать данные.

1. Завершение транзакции

Когда все действия, составляющие транзакцию успешно выполнены или возникла ошибка, транзакция должна быть завершена, для того, чтобы база данных находилась в непротиворечивом состоянии. Для этого есть два SQL-выражения:

- COMMIT - сохраняет внесенные транзакцией изменения в базу данных. Это означает, что транзакция завершена успешно.
- ROLLBACK - откат транзакции. Транзакция завершается и никаких изменений в базу данных не вносится. Данная операция выполняется при возникновении ошибки при выполнении операции (например, при невозможности обновить запись).

1. Управление транзакциями в Delphi

Прежде всего, транзакции в Delphi бывают явные и неявные.

Явная транзакция - это транзакция, начатая и завершенная с помощью методов объекта DataBase: StartTransaction, Commit, RollBack. После начала явной транзакции, все изменения, вносимые в данные относятся к этой транзакции.

Другого способа начать явную транзакцию, нежели с использованием DataBase, нет. (Точнее говоря, такая возможность есть, но это потребует обращения к функциям API InterBase. Однако, это уже достаточно низкоуровневое программирование.) Следовательно, в рамках одного соединения нельзя начать две транзакции.

Неявная транзакция стартует при модификации данных, если в данный момент нет явной транзакции. Неявная транзакция возникает, например, при выполнении метода Post для объектов Table и Query. То есть, если Вы отредактировали запись, в DBGrid и переходите на другую запись, то это влечет за собой выполнение Post, что, в свою очередь, приводит к началу неявной транзакции, обновлению данных внутри транзакции и ее завершению. Важно отметить, что неявная транзакция, начатая с помощью методов Post, Delete, Insert, Append и т.д. заканчивается автоматически.

Для модификации данных может использоваться и PassThrough SQL - SQL-выражение, выполняемое с помощью метода ExecSQL класса TQuery. Выполнение модификации через PassThrough SQL также приводит к старту неявной транзакции. Дальнейшее поведение транзакции, начатой таким путем, определяется значением параметра SQLPASSTHRU MODE для псевдонима базы данных (или тот-же параметр в св-ве Params объекта DataBase). Этот параметр может принимать три значения:

- SHARED AUTOCOMMIT - слово SHARED указывает на то, что оба вида транзакций(через Passthrough SQL и через методы TTable и TQuery) разделяют одно и то же соединение к базе данных. Слово AUTOCOMMIT указывает на то, что неявная транзакция, начатая через Passthrough SQL, завершается после выполнения действия по модификации данных (автоматически выполняется COMMIT).
- SHARED NOAUTOCOMMIT - отличается от предыдущего тем, что неявная транзакция, начатая через Passthrough SQL, не завершается после выполнения, ее нужно явно завершить, выполнив SQL-выражение “COMMIT”.
- NOT SHARED - транзакции разных типов работают через разные соединения с базой. Данное значение параметра подразумевает также NOAUTOCOMMIT. То есть все неявные PassthroughSQL-транзакции нужно завершать явно - выполняя SQL-выражение “COMMIT” для Passtrough SQL.

Рассмотрим возможные сценарии поведения транзакций при разных значениях параметра.

В первом случае, если нет в данный момент начатой транзакции, то попытка модификация данных методами TTable или TQuery, как и выполнение через Passtrough SQL какой-либо операции приведет к старту неявной транзакции. После выполнения, такая транзакция будет автоматически завершена (если не возникло ошибки по ходу транзакции). Если уже имеется начатая явно (метод StartTransaction объекта DataBase) транзакция, то изменения будут проходить в ее контексте. Все транзакции используют одно и то-же соединение.

Во втором случае все происходит, как в первом. Отличие в том, что неявная PassthroughSQL-транзакция не завершается, пока не будет выполнена команда “COMMIT”.

В третьем случае, при выполнении команды Passthrough SQL, будет установлено еще одно соединение, начата неявная транзакция и выполнены действия по модификации данных. Транзакция не будет завершена, пока не будет выполнена команда “COMMIT”. Наличие транзакции, начатой явно с помощью DataBase никак не отразится на ходе выполнения PassthroughSQL-транзакции. Пока PassthroughSQL-транзакция не завершится, изменения, внесенные ей, не будут видны в объектах Table и Query, работающих через другое соединение. PassthroughSQL-транзакции можно рассматривать в некотором смысле, как транзакции из другого приложения.

Взаимодействие транзакций данной программы с транзакциями из других приложений определяется свойством TransIsolation объекта DataBase. Для InterBase имеет смысл два значения: tiReadCommitted и tiRepeatableRead. Выполнение метода StartTransaction в этих двух случаях равносильно выполнению SQL-выражений, соответственно:

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL READ COMMITTED

и

SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT