Магия транзакций в Bitrix: Как заставить ваши данные держаться как вкопанные

от автора

? Привет! Сегодня мы поговорим о том, как использовать транзакции в Bitrix, чтобы обеспечить целостность данных. А также, что такое вложенные транзакции и для чего они нужны.


? Тайны Транзакций

Версия main 22.200.0 принесла с собой нечто волшебное — вложенные транзакции в Bitrix! Зачем они нужны? Давайте посмотрим на этот процесс с точки зрения магии.

Untitled

Транзакции — это ваше надежное магическое заклинание, которое гарантирует, что ваши множественные изменения в базе данных будут выполнены целиком или полностью отменены, если что-то пошло не так.

С появлением вложенных транзакций, мы добавляем новый элемент магии. Теперь, при повторном старте транзакции, создаются точки сохранения. Вложенные транзакции поддерживаются, но решение о вложенных откатах лучше оставить конечному сценарию.

? Для чего нужны транзакции?

  • Транзакции позволяют объединять несколько запросов в единую конструкцию. Она гарантирует, что запросы будут выполнены как единое целое.

  • Транзакции — это нечто большее, чем просто блок кода. Это своего рода защитный механизм, который помогает вам избежать проблем с целостностью данных. Они гарантируют, что ваши операции с базой данных будут либо успешными, либо полностью отменены, если что-то пошло не так.

? Как это было раньше?

Прежде чем волшебство транзакций появилось в Bitrix, разработчики вынуждены были прибегать к тайным приемам.

Создание таблицы-шпиона, где каждое изменение регистрировалось. Это позволяло восстановить предыдущее состояние при ошибке.

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

? Волшебство в действии: Как это работает в коде?

<?php  $application = \Bitrix\Main\Application::getInstance(); $connection = $application->getConnection();  try {     $connection->startTransaction();        // Ваш волшебный код здесь...        $connection->commitTransaction(); } catch (\Throwable $e) {     $connection->rollbackTransaction();     throw $e; }

Окей, давайте рассмотрим пример использования транзакций на практике. Допустим, у нас есть сущность, назовем ее ExampleTable. Мы хотим изменить поле NAME у записи с кодом ‘example’. При этом, мы также хотим добавить новую запись в другую таблицу — AnotherTable.
В этом кусочке кода транзакция начинается, операции выполняются, и если что-то идет не так, мы можем легко и безопасно откатить все изменения.

<?php  $application = \Bitrix\Main\Application::getInstance(); $connection = $application->getConnection(); // Получаем соединение с базой данных  try {     // Начинаем транзакцию     $connection->startTransaction();      // Получаем из сущности некий объект     $object = \Example\ExampleTable::query()         ->where('CODE', 'example')         ->addSelect('NAME')         ->fetchObject();      if ($object) {         $object->set('NAME', 'Пример записи'); // Меняем поле `NAME` у объекта         $result = $object->save(); // Сохраняем         if (!$result->isSuccess()) {             throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');         }                      // Пробуем добавить второй элемент в другую таблицу         $anotherObject = \Example\AnotherTable::createObject();         $anotherObject->set('TITLE', 'New title');         $result = $anotherObject->save(); // Сохраняем         if (!$result->isSuccess()) {             // В случае ошибки откатываем транзакцию, и первый элемент не сохранится             throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');         }          // Успешно завершаем транзакцию         $connection->commitTransaction();     } else {         throw new \Bitrix\Main\SystemException('Объект не найден.');     } } catch (\Throwable $e) {     // Если произошла ошибка, отменяем транзакцию     $connection->rollbackTransaction();     throw $e; }

Волшебство становится еще увлекательнее с появлением вложенных транзакций. Давайте еще усложним пример, который покажет, как они работают.

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

<?php  $application = \Bitrix\Main\Application::getInstance(); $connection = $application->getConnection();  try {     // Начинаем транзакцию     $connection->startTransaction();      // Получаем из сущности некий объект     $object = \Example\ExampleTable::query()         ->where('CODE', 'example')         ->addSelect('NAME')         ->fetchObject();      if ($object) {         $object->set('NAME', 'Пример записи'); // Меняем поле `NAME` у объекта         $result = $object->save(); // Сохраняем         if (!$result->isSuccess()) {             throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');         }                      // Пробуем добавить второй элемент в другую таблицу         $anotherObject = \Example\AnotherTable::createObject();         $anotherObject->set('TITLE', 'New title');         $result = $anotherObject->save(); // Сохраняем         if (!$result->isSuccess()) {             // В случае ошибки откатываем транзакцию, и первый элемент не сохранится             throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');         }          // Вложенная транзакция         $connection->startTransaction();          // Создаем объект для вложенной таблицы         $nestedObject = \Example\NestedTable::createObject();         $nestedObject->set('DESCRIPTION', 'Nested description');         $result = $nestedObject->save();         if (!$result->isSuccess()) {             // В случае ошибки откатываем вложенную транзакцию              // Т.к у битрикса действует парадигма "вложенные роллбеки не поддерживаются"              // Происходит исключение \Bitrix\Main\DB\TransactionException             $connection->rollbackTransaction();         } else {             // Успешно завершаем вложенную транзакцию             $connection->commitTransaction();         }                  // Успешно завершаем основную транзакцию         $connection->commitTransaction();     } else {         throw new \Bitrix\Main\SystemException('Объект не найден.');     } } catch (\Bitrix\Main\DB\TransactionException $e) {     // ROLLBACK вложенной транзакции     // Тут мы можем решить, что делать, если вложенная транзакция не прошла } catch (\Throwable $e) {     // Если произошла ошибка, отменяем транзакцию     $connection->rollbackTransaction();     throw $e; } 

? Что делают методы управления транзакциями?

  • $connection->startTransaction();: Этот метод начинает транзакцию, позволяя группировать несколько операций в единое целое.

  • $connection->commitTransaction();: Вызов этого метода фиксирует все изменения, сделанные в рамках транзакции, и отправляет их в базу данных.

  • $connection->rollbackTransaction();: Если произошла ошибка или необходимо отменить все изменения в рамках транзакции, этот метод откатывает все операции.

?️‍?️ Дополнительные тайны:

  • Транзакции применимы не только к save(), но и к другим методам ORM, таким как update(), add(). А также, к SQL запросам через $connection->query()

  • Если хотите углубиться в тайны магии транзакций, загляните в официальную документацию Bitrix

? Завершение:

Таким образом, мы научились использовать волшебство транзакций в Bitrix, обеспечивая целостность данных. Если у вас есть свои волшебные методы или вопросы, делитесь в комментариях!


ссылка на оригинал статьи https://habr.com/ru/articles/775398/