Транзакции и автоматическая фиксация изменений
Теперь вы знаете, как подключаться к базам данных посредством PDO. Но перед
тем как выполнять запросы, вам необходимо понять, как PDO управляет
транзакциями. Если вы прежде не сталкивались с транзакциями, они обладают
четырьмя главными свойствами, это Атомарность, Согласованность, Изолированность
и Долговечность (ACID). Говоря простым языком, любые действия, совершенные в
рамках транзакции, гарантированно будут выполнены безопасно для базы данных, и
на них не повлияют другие подключения к этой базе, даже если эти действия
совершаются в несколько этапов. Транзакционные операции можно отменять по
запросу (если транзакция ещё не зафиксирована), что упрощает обработку
ошибок в скриптах.
Механизм транзакций реализован путём "временного сохранения" всех изменений и
дальнейшего применения этих изменений, как единого целого. Это позволяет
добиться резкого увеличения эффективности подобных изменений. Другими словами,
транзакции могут сделать ваши скрипты более быстрыми и потенциально более стабильными
(но для этого необходимо корректно использовать этот механизм).
К сожалению, не все базы данных поддерживают транзакции, поэтому PDO
при создании подключения работает в режиме так называемой "автоматической
фиксации". Режим автофиксации означает, что каждый запрос к базе данных, который
вы выполняете, неявно заключается в транзакцию, если СУБД их поддерживает.
Если база данных не поддерживает этот механизм, запрос обрабатывается без
транзакции. Чтобы явно обозначить начало транзакции, вы должны использовать
метод PDO::beginTransaction(). Если драйвер не
поддерживает механизм транзакций, будет выброшено исключение PDOException
(вне зависимости от выбранного способа обработки ошибок: подобные ситуации -
это всегда серьёзная недоработка). После того, как вы совершите транзакцию, вы можете
зафиксировать её методом PDO::commit() или откатить методом
PDO::rollBack(), в зависимости от того, успешно выполнен ваш
код внутри транзакции или нет.
Внимание
PDO проверяет возможность использования транзакций только на уровне драйвера.
Если по каким-то причинам механизм транзакций недоступен, но сервер баз данных
принял запрос на открытие транзакции,
PDO::beginTransaction() вернёт true
без ошибок.
В качестве примера может быть попытка использования транзакций в таблицах
MyISAM базы данных MySQL.
При завершении работы скрипта или при закрытии соединения, PDO автоматически
откатывает все незавершённые транзакции. Это делается, чтобы предотвратить
нарушения целостности базы данных в случаях, когда скрипт неожиданно прерывает
работу. Если вы явно не зафиксировали изменения, предполагается, что что-то
пошло не так. Поэтому откат изменений - наиболее безопасный выход из ситуации.
Внимание
Изменения будут откачены автоматически, только если транзакция открыта методом
PDO::beginTransaction(). Если транзакцию открыть вручную в
тексте запроса, PDO об этом никак не узнает, и, соответственно, не сможет принять
мер, если произойдёт что-то плохое.
Пример #1 Выполнение пакета изменений в рамках транзакции
В следующем примере предположим, что мы создаём несколько записей для нового
сотрудника с номером ID 23. Помимо ввода основной информации необходимо
записать его зарплату. Довольно просто сделать два отдельных обновления таблиц,
однако путём заключения этих запросов в рамки
PDO::beginTransaction() и
PDO::commit() мы сможем гарантировать, что никто не увидит
этих изменений, пока все они не будут завершены. Если что-то пойдёт не так,
catch-блок откатит все изменения с начала транзакции и напечатает сообщение об
ошибке.
<?php
try {
$dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
array(PDO::ATTR_PERSISTENT => true));
echo "Подключились\n";
} catch (Exception $e) {
die("Не удалось подключиться: " . $e->getMessage());
}
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();
} catch (Exception $e) {
$dbh->rollBack();
echo "Ошибка: " . $e->getMessage();
}
?>
Вы никак не ограничены в количестве запросов в рамках транзакции; вы также
можете выполнять сложные запросы, чтобы извлечь данные, а затем использовать
их для создания других запросов на обновление и извлечение данных; если
транзакция активна, вы можете быть уверены, что никто не сможет изменить ваши
данные, пока вы с ними работаете. За дополнительной информацией о транзакциях
обращайтесь к документации к вашему серверу баз данных.