Транзакции
Drupal также поддерживает транзакции, включая прозрачный запасной вариант для баз данных, которые не поддерживают транзакции. Однако транзакции могут быть довольно сложными, если вы попытаетесь запустить две транзакции одновременно. Поведение в этом случае также зависит от базы данных.
Аналогичная проблема существует с вложенными замками в C / C ++. Если код уже получил блокировку A и пытается получить блокировку A, код будет заблокирован. Если вы пишете код, который проверяет, имеет ли он уже блокировку и не пытается ее снова получить, вы избегаете тупиковой ситуации, но можете снять блокировку преждевременно.
В SQL у нас та же проблема. Если ваш код уже находится в транзакции, запуск новой транзакции имеет неожиданное и печальное последствие совершения текущей транзакции и запуска новой.
Java решает проблему вложенности с помощью своих блокировок путем реализации поддержки структуры вложенности, аналогичной той, что мы тестируем ниже. Java позволяет помечать функции как «синхронизированные», что заставляет функцию ждать получения блокировки перед запуском и снимать блокировку, когда она больше не нужна. Если одна синхронизированная функция вызывает другую в том же классе, Java отслеживает вложение блокировки. Внешняя функция получает блокировку, внутренняя функция не выполняет операций блокировки, а внешняя функция снимает блокировку при возврате.
Хотя мы не можем объявить функции «transactional» в PHP, мы можем эмулировать логику вложенности Java, используя объекты с конструкторами и деструкторами. Функция просто вызывает «$transaction = $connection->startTransaction()» в качестве первой (или почти первой) операции, чтобы сделать себя transactional. Если одна transactional функция вызывает другую, наш уровень абстракции транзакций вкладывает их, не выполняя transactional операций (насколько видит база данных) внутри внутренних уровней вложенности.
Чтобы начать новую transaction, просто вызовите $transaction = $connection->startTransaction(); в вашем собственном коде. Transaction будет оставаться открытой до тех пор, пока переменная $transaction остается в области видимости. Когда $transaction уничтожена, transaction будет зафиксирована. Если ваша transaction вложена в другую, Drupal будет отслеживать каждую transaction и фиксировать самую внешнюю transaction только тогда, когда последний объект transaction выйдет из области видимости, то есть все соответствующие запросы успешно завершены.
Вы должны присвоить возвращаемое значение $connection->startTransaction(); к переменной, как в примере. Если вы вызываете метод, не назначая возвращаемое значение переменной, ваша transaction будет зафиксирована мгновенно, что сделает ее бесполезной.
Откат transaction управляется объектом соединения ($connection->rollBack()) в Drupal 8, но обычно его следует выполнять с помощью метода-оболочки транзакции ($action->rollBack()). Причина, по которой это должно быть сделано с помощью метода rollBack() transaction, заключается в том, что она откатывает эту transaction на основе ее имени, где $connection->rollBack() по умолчанию задает имя drupal_transaction и может давать нежелательные результаты при использовании с вложенными транзакциями.
Пример:
function my_transaction_function() { // The transaction opens here. $transaction = $connection->startTransaction(); try { $id = $connection->insert('example') ->fields([ 'field1' => 'mystring', 'field2' => 5, ]) ->execute(); my_other_function($id); return $id; } catch (Exception $e) { $transaction->rollBack(); watchdog_exception('my_type', $e); } // You can let $transaction go out of scope here and the transaction will // automatically be committed if it wasn't already rolled back. // However, if you have more work to do, you will want to commit the transaction // yourself, like this: $transaction->commit(); // More code here that is outside of the transaction. } function my_other_function($id) { // The transaction is still open here. if ($id % 2 == 0) { $connection->update('example') ->condition('id', $id) ->fields(['field2' => 10]) ->execute(); } }
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.