Задача
Вы пишете программу, в которой необходимо использовать транзакции.
Решение
Используйте абстракцию транзакции в API для вашего языка программирования, если такая существует. Если нет, используйте обычный механизм выполнения запроса в API для непосредственного запуска транзакционных предложений SQL.
Обсуждение
При интерактивном запуске запросов из mysql (как в примерах предыдущего раздела) вы можете увидеть, успешно ли выполнено предложение, и на основе этой информации определить, следует зафиксировать или откатить изменения. Если же вы выполняете запросы в неинтерактивном сценарии SQL, хранящемся в файле, то все не так просто. У вас нет возможности выполнять фиксацию и откат в зависимости от результата выполнения предложения, так как в MySQL нет инструкции IF/THEN/ELSE, которая управляла бы логикой сценария. (Есть функция IF(), но это не то же самое.) Поэтому обработка транзакций обычно производится в программе, где можно применить средства базового языка для выявления ошибок и выполнения соответствующего действия. В этом разделе представлены основные сведения о том, как это делать. Следующие разделы посвящены особенностям реализации транзакций в программных интерфейсах Perl, PHP, Python и Java.
Все API поддерживают транзакции хотя бы в том смысле, что вы можете явно создавать транзакционные предложения SQL, такие как BEGIN и COMMIT. Однако некоторые API предлагают абстракцию транзакции, которая позволяет управлять поведением транзакций без непосредственного обращения к SQL. Этот подход скрывает детали и обеспечивает переносимость на другие базы данных, поддерживающие транзакции, синтаксис SQL которых может несколько отличаться от исходного. MySQL-интерфейсы Perl, Python и Java предоставляют такую абстракцию. В PHP вам придется создавать предложения SQL самостоятельно.
В следующих разделах для иллюстрации программной реализации транзакций рассматривается один и тот же пример. В них используется таблица t, исходное содержимое которой показывает, сколько денег было у каждого из двух человек:
+------+------+
| name | amt |
+------+------+
| Eve | 10 |
| Ida | 0 |
+------+------+
Примером транзакции будет простая финансовая операция перевода денег, использующая два предложения UPDATE, чтобы передать Иде 6 долларов Евы:
UPDATE money SET amt = amt - 6 WHERE name = 'Eve';
UPDATE money SET amt = amt + 6 WHERE name = 'Ida';
В результате таблица станет такой:
+------+------+
| name | amt |
+------+------+
| Eve | 4 |
| Ida | 6 |
+------+------+
Необходимо включить оба предложения в транзакцию, чтобы быть уверенным в том, что они вступают в силу одновременно. В отсутствие транзакции деньги Евы просто пропадут, если не выполнится второе предложение. Если же использовать транзакцию, то в случае невыполнения предложения таблица останется неизмененной.
Программы примеров для каждого языка хранятся в каталоге transactions дистрибутива recipes. Если сравнить их, то станет видно, что все они используют одну и ту же схему обработки транзакций:
• Предложения транзакции группируются в управляющую структуру вместе с операцией фиксации.
• Если статус управляющей структуры указывает на то, что она не была успешно завершена, транзакция откатывается.
Эту логику можно выразить следующим образом (block – это управляющая структура, используемая для группировки предложений):
block:
statement 1
statement 2
...
statement n
commit
if the block failed:
roll back
В Perl управляющая структура – это блок eval, который или выполняется успешно, или завершается с ошибкой, возвращая ее код. Python и Java используют блок try, который выполняется до конца, если транзакция была успешной. В случае ошибки генерируется исключение, которое инициирует выполнение соответствующего блока обработки ошибок для отката транзакции. В PHP нет подобных конструкций, но вы можете получить тот же результат, выполняя предложения транзакции и ее фиксацию в функции. Если функция завершается с ошибкой, выполняется откат.
Преимущество такого структурирования кода в том, что минимизируется количество проверок, необходимых для определения необходимости отката. Альтернативный вариант – проверка результата каждого предложения втранзакции и откат при ошибках отдельных предложений, быстро сделает ваш код беспорядочным и трудночитаемым.
При выполнении отката в языке, генерирующем исключения, необходимо помнить о том, что сам откат может не удаться, что вызовет новое исключение. Если вы не хотите иметь с этим дело, выполняйте откат в отдельном блоке с пустым обработчиком исключения. Именно так поступают программы на Perl, Python и Java.