Транзакции и автоматическая фиксация изменений

Теперь вы знаете, как подключаться к базам данных посредством 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();
}
?>

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

Добавить

Примечания пользователей 3 notes

up
6
hooby404 at gmail dot com
2 years ago
> You're not limited to making updates in a transaction; you can also issue complex
> queries to extract data, and possibly use that information to build up more updates
> and queries; while the transaction is active, you are guaranteed that no one else can
> make changes while you are in the middle of your work. For further reading on
> transactions, refer to the documentation provided by your database server.

This only holds true if you specifically do "SELECT .... FOR UPDATE".

Without the "FOR UPDATE" part, when two transactions run at the same time, the second transaction could change a value AFTER the first transaction read it, but BEFORE the first transaction used it for updates.

Without the "FOR UPDATE" part you are absolutely NOT GUARANTEED that no one else can make changes while you are in the middle of your work.
up
5
harl at gmail dot com
6 years ago
Some DBMSs allow DDL (table creation/alteration) within transactions, some do not. Asking "Does my DBMS allow DDL within transactions without forcing a commit?" gives the following example answers:

CUBRID: Yes
DB2 UDB: Yes
Firebird: Yes
Informix: Yes
MySQL: No
Oracle: No (although schema upgrades can be rolled out using "edition-based redefinition")
PostgreSQL: Yes
SQLite: Yes
SQL Server: Sometimes, depending on isolation level, type of command, etc.
Sybase: Yes
up
4
pasamio at gmail dot com
12 years ago
Typically data definition language clauses (DDL) will trigger the database engine to automatically commit:
http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html

Many other databases (e.g. Oracle) will implicitly commit before and after running DDL statements.
To Top