Блог Горошко Андрея 1C-Битрикс Транзакции в Битрикс: Как обеспечить целостность данных с startTransaction, commit и rollback

Транзакции в Битрикс: Как обеспечить целостность данных с startTransaction, commit и rollback

Средний рейтинг
Еще нет оценок

Представьте ситуацию: вам нужно списать деньги с одного счета и зачислить их на другой.

Это две отдельные SQL-операции (UPDATE).

Что, если первая выполнится успешно, а вторая — с ошибкой? Деньги «повиснут в воздухе».

Чтобы предотвратить такие проблемы, существуют транзакции.

Транзакция — это механизм базы данных, который гарантирует, что группа из нескольких SQL-операций будет выполнена по принципу «всё или ничего».

Либо все операции в группе успешно завершаются, либо все они полностью отменяются.

Когда нужно использовать транзакции?

  • Когда операция состоит из двух и более взаимосвязанных запросов на изменение данных (INSERT, UPDATE, DELETE).
  • При проведении заказов (списание со склада, создание оплаты).
  • При сложных операциях импорта данных.
  • При любых действиях, где частичное выполнение недопустимо.

Как работать с транзакциями в Битрикс D7

Современный способ — использовать методы объекта соединения D7.

Основной шаблон:

use Bitrix\Main\Application;
use Bitrix\Main\DB\Connection;

/** @var Connection $connection */
$connection = Application::getConnection();

try {
    // 1. Начинаем транзакцию
    $connection->startTransaction();

    // --- Выполняем группу операций ---
    // Например, обновление элемента ORM
    $result1 = \My\Table::update(1, ['QUANTITY' => 5]);
    if (!$result1->isSuccess()) {
        // Если первая операция не удалась, выбрасываем исключение
        throw new \Exception(implode(', ', $result1->getErrorMessages()));
    }
    
    // И еще одна операция
    $result2 = \My\AnotherTable::add(['ITEM_ID' => 1, 'STATUS' => 'PROCESSED']);
    if (!$result2->isSuccess()) {
        // Если вторая операция не удалась, выбрасываем исключение
        throw new \Exception(implode(', ', $result2->getErrorMessages()));
    }
    // ---------------------------------

    // 2. Если все прошло успешно, "коммитим" транзакцию
    $connection->commitTransaction();
    
    echo "Все операции успешно выполнены!";

} catch (\Exception $e) {
    // 3. Если на любом этапе возникло исключение, "откатываем" транзакцию
    $connection->rollbackTransaction();
    
    echo "Произошла ошибка, все изменения отменены: " . $e->getMessage();
}

Разбор шагов:

  1. $connection->startTransaction(): Подает базе данных команду начать новую транзакцию. Все последующие SQL-запросы будут выполняться «виртуально» в рамках этой транзакции.
  2. Блок try: Здесь размещается вся ваша бизнес-логика. Важно проверять результат каждой операции и в случае неудачи немедленно прерывать выполнение, выбрасывая исключение.
  3. $connection->commitTransaction(): Если код дошел до этой строки, значит, все операции в блоке try прошли без ошибок. Эта команда говорит базе данных: «Все в порядке, сохрани все изменения, сделанные с начала транзакции».
  4. Блок catch и $connection->rollbackTransaction(): Если в блоке try было выброшено исключение, выполнение переходит в catch. Команда rollbackTransaction говорит базе данных: «Отмени все изменения, сделанные с начала этой транзакции».

Старый API ($DB)

Аналогичные методы есть и у глобального объекта $DB:

  • $DB->StartTransaction()
  • $DB->Commit()
  • $DB->Rollback()

Логика их использования точно такая же.

Вложенные транзакции

D7 ORM поддерживает вложенные транзакции. Если вы вызовете startTransaction() внутри уже начатой транзакции, Битрикс создаст «точку сохранения» (SQL SAVEPOINT). commitTransaction() во вложенной транзакции ничего не сделает, а rollbackTransaction() откатит изменения только до этой точки сохранения. Это сложный механизм, и в 99% случаев лучше избегать вложенных транзакций, выстраивая логику линейно.

ДействиеПоведение
startTransaction() внутри другойСоздаёт точку сохранения
commitTransaction() вложеннойНе сохраняет в БД, ждёт внешнего коммита
rollbackTransaction() вложеннойОткат до SAVEPOINT, выбрасывает TransactionException

Рекомендация: избегайте вложенных откатов. Управление ошибками должно быть в основной транзакции.

Обработка ошибок во вложенных транзакциях

use Bitrix\Main\DB\TransactionException;

function updateAccounts(int $userId, Connection $db) {
    try {
        $db->startTransaction();
        // Логика обновления
        $db->commitTransaction();
    } catch (Throwable $e) {
        $db->rollbackTransaction();
        throw $e;
    }
}

function updateOrders(int $userId, Connection $db) {
    try {
        $db->startTransaction();
        // Логика обновления
        $db->commitTransaction();
    } catch (Throwable $e) {
        $db->rollbackTransaction();
        throw $e;
    }
}

$db = Application::getConnection();

try {
    $db->startTransaction();

    updateOrders($userId, $db);    // Вложенная
    updateAccounts($userId, $db);  // Вложенная

    $db->commitTransaction();
} catch (TransactionException $e) {
    $db->rollbackTransaction(); // Откат всей транзакции
} catch (Throwable $e) {
    $db->rollbackTransaction();
    throw $e;
}

Практические советы

  • Делайте транзакции короткими — это снижает риск блокировок
  • Проверяйте результат каждой операции (isSuccess())
  • Логируйте ошибки и исключения
  • Не смешивайте ORM и SQL без крайней необходимости
  • Если используете DataManager::getConnectionName, убедитесь, что транзакция охватывает нужное соединение

Архитектурные нюансы

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

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

Всегда оборачивайте группы взаимосвязанных add, update или delete операций в блок try…catch с startTransaction и commit/rollback.

Это единственный способ гарантировать целостность и консистентность вашей базы данных.


транзакции Битрикс, startTransaction, commitTransaction, rollbackTransaction, целостность данных, D7, Application::getConnection, SQL.

Мой рейтинг:

Добавить комментарий

Related Post

Bitrix D7 ORM: Мастер-класс по ElementTable::getList для выборки элементовBitrix D7 ORM: Мастер-класс по ElementTable::getList для выборки элементов

Средний рейтинг 5 из 5 звезд. 2 голосов. С появлением ядра D7 в 1С-Битрикс у разработчиков появился новый, объектно-ориентированный способ работы с базой данных — ORM (Object-Relational Mapping). Этот подход

Работа с пользователями в Битрикс: CUser::GetList, Add, UpdateРабота с пользователями в Битрикс: CUser::GetList, Add, Update

Средний рейтинг Еще нет оценок Управление пользователями — одна из ключевых задач любой CMS. В Битрикс для этого предназначен класс CUser. Глобальный объект $USER (экземпляр этого класса) доступен на каждой странице и содержит

Создание простого компонента Битрикс с нуля: Пошаговое руководствоСоздание простого компонента Битрикс с нуля: Пошаговое руководство

Средний рейтинг Еще нет оценок Создание собственных компонентов — это основа профессиональной разработки на Битрикс. Это позволяет инкапсулировать логику, делать код переиспользуемым и следовать стандартам платформы. Давайте создадим простой компонент