Files
2026-07-27 12:58:44 +03:00

3.2 KiB

Транзакции

Назад к разделу БД

Транзакции гарантируют, что группа изменений применится «всё или ничего».


Методы

DB::startTransaction();   // начать
DB::commit();             // подтвердить
DB::rollback();           // откатить

Поддерживаются вложенные транзакции (через nested_transactions — внутренние уровни эмулируются savepoint-логикой менеджера транзакций).

Действие после commit

Внешний HTTP, отправку писем, публикацию в соцсеть и другие необратимые действия не запускайте до подтверждения БД. Зарегистрируйте callback:

DB::startTransaction();
try {
    DB::Insert($orders, $order);
    DB::afterCommit(function () use ($order) {
        OrderNotifications::created($order);
    });
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

Внутри транзакции callback выполняется после успешного внешнего commit(). При rollback() он удаляется. Если активной транзакции нет, DB::afterCommit() выполняет callback сразу. Исключение callback журналируется и не может отменить уже подтверждённую транзакцию, поэтому интеграция должна быть идемпотентной.


Базовый паттерн

Всегда оборачивайте тело в try/catch и откатывайте на ошибке:

DB::startTransaction();
try {
    DB::Insert($orders, $order);
    $orderId = (int) DB::insertId();

    foreach ($items as $item) {
        $item['order_id'] = $orderId;
        DB::Insert($orderItems, $item);
    }

    DB::Update($stock, array('qty' => DB::sqlEval('qty - 1')), 'id = %i', $productId);

    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;   // или обработать/залогировать
}

Когда нужны

  • Несколько связанных записей должны примениться вместе (заказ + позиции + остатки).
  • Пакетные операции, где частичный результат недопустим.
  • Перенос/пересчёт данных.

Когда не нужны

  • Одиночный INSERT/UPDATE/DELETE — он и так атомарен.
  • Только чтение (SELECT).

Подводные камни

  • Не забывайте commit() — без него по завершении запроса изменения откатятся.
  • Не смешивайте с DDL (ALTER, CREATE) — в MySQL они вызывают неявный commit.
  • Держите транзакцию короткой: не делайте внутри неё внешних HTTP-вызовов и долгих операций — это держит блокировки.