Обзор SQL-выражения для управления
транзакциями
Запуск транзакции Завершение транзакции Управление транзакциями
в Delphi
Обзор
Все операции, выполняемые с данными на SQL сервере,
происходят в контексте транзакций. Транзакция - это групповая операция,
т.е. набор действий с базой данных; самым существенным для этих действий
является правило либо все, либо ни чего. Если во время выполнения
данного набора действий, на каком-то этапе невозможно произвести очередное
действие, то нужно выполнить возврат базы данных к начальному состоянию
(произвести откат транзакции). Таким образом (при правильном планировании
транзакций), обеспечивается целостность базы данных. В данном уроке объясняется,
как начинать, управлять и завершать транзакции с помощью SQL выражений.
А так же рассматривается вопрос об использовании транзакций в приложениях,
созданных в Delphi. Вся приведенная
информация касается InterBase.
SQL-выражения для управления
транзакциями
Для управления транзакциями имеется три выражения:
SET TRANSACTION
- Начинает транзакцию и определяет ее поведение.
COMMIT-
Сохраняет изменения,
внесенные транзакцией, в базе данных и завершает транзакцию.
ROLLBACK - Отменяет
изменения, внесенные транзакцией, и завершает транзакцию.
Запуск транзакции
Выполнять транзакции можно, например, из 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 могут
модифицировать таблицу, остальные - только выбирать данные.
Завершение транзакции
Когда все действия, составляющие транзакцию успешно выполнены или возникла
ошибка, транзакция должна быть завершена, для того, чтобы база данных находилась
в непротиворечивом состоянии. Для этого есть два SQL-выражения:
COMMIT - сохраняет внесенные
транзакцией изменения в базу данных. Это означает, что транзакция завершена
успешно.
ROLLBACK - откат транзакции.
Транзакция завершается и никаких изменений в базу данных не вносится. Данная
операция выполняется при возникновении ошибки при выполнении операции (например,
при невозможности обновить запись).
Управление транзакциями в
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
Знаете ли Вы, что абстрактный класс - это класс, содержащий хотя бы один виртуальный метод. Абстрактные классы не бывают изолированными, т.е. всегда абстрактный класс должен быть наследуемым. Поскольку у чисто виртуального метода нет тела, то создать объект абстрактного класса невозможно. Абстрактным классом можно назвать класс, специально определенный для обеспечения наследования характеристик порожденными классами.