Красномовні транзакції ORM від Laravel


96

Eloquent ORM досить приємний, хоча мені цікаво, чи існує простий спосіб налаштування транзакцій MySQL за допомогою innoDB так само, як PDO, чи мені доведеться розширити ORM, щоб це стало можливим?

Відповіді:


165

Ви можете зробити це:

DB::transaction(function() {
      //
});

Усе усередині Закриття виконується в межах транзакції. У разі виникнення винятку він автоматично відкочується.


1
Усередині закриття я можу викликати запити в класі? Це буде працювати?
Рафаель Суфраз

На жаль, це не працює для мене, якщо я створюю екземпляр різних моделей, які зберігають записи у своїх власних відповідних методах.
Volatil3,

Якщо я вловлюю виняток усередині моєї транзакції (для створення повідомлень про помилки тощо), чи потрібно повторно випускати виняток, щоб відбувся відкат?
alexw

3
Хороша відповідь, але мене наздогнало кілька речей: 1. Вам потрібно додати "use DB;" для цього, наприклад, у верхній частині файлу вашої моделі 2. На відміну від JS, ви не отримуєте доступу до локальних змінних у батьківській області, якщо ви явно не передаєте їх, вам потрібно додати конструкцію "use" таким чином ... DB :: транзакція (function () use ($ user) {... stuffs reference $ user ...});
Полсонбі

Discussed in more detail hereпосилання мертве.
tomloprod

100

Якщо вам не подобаються анонімні функції:

try {
    DB::connection()->pdo->beginTransaction();
    // database queries here
    DB::connection()->pdo->commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::connection()->pdo->rollBack();
}

Оновлення : Для laravel 4 pdoоб’єкт більше не є загальнодоступним, тому:

try {
    DB::beginTransaction();
    // database queries here
    DB::commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::rollBack();
}

15
Ви також можете використовувати способи швидкого доступу DB::beginTransaction()& DB::commit()& DB::rollback(). Це було б трохи чистіше.
Флорі

2
Будь ласка, оновіть, щоб використовувати пропозицію @Flori. Це чистіше. Крім того, переміщення нової відповіді вгору зробить вашу відповідь менш заплутаною. Я використав перший метод, перш ніж повернутися до другого.
frostymarvelous

Для старішої версії Laravel вам може знадобитися:DB::connection()->getPdo()->beginTransaction();
замість цього

Я особисто вважаю, що DB::transactionіз зворотним викликом ще чистіше, але недоліком є ​​те, що якщо вам потрібно вказати різні обробники для різних винятків, вам доведеться повернутися, щоб спробувати / зловити техніку
OzzyTheGiant

33

Якщо ви хочете використовувати Eloquent, ви також можете використовувати це

Це лише зразок коду з мого проекту

        /* 
         * Saving Question
         */
        $question = new Question;
        $questionCategory = new QuestionCategory;

        /*
         * Insert new record for question
         */
        $question->title = $title;
        $question->user_id = Auth::user()->user_id;
        $question->description = $description;
        $question->time_post = date('Y-m-d H:i:s');

        if(Input::has('expiredtime'))
            $question->expired_time = Input::get('expiredtime');

        $questionCategory->category_id = $category;
        $questionCategory->time_added = date('Y-m-d H:i:s');

        DB::transaction(function() use ($question, $questionCategory) {

            $question->save();

            /*
             * insert new record for question category
             */
            $questionCategory->question_id = $question->id;
            $questionCategory->save();
        });

question->idВираз на зворотний виклик транзакції повертає нуль.
Christos Papoulas

@ChristosPapoulas ти мав на увазі, ми не можемо отримати ідентифікатор автоматичного збільшення в транзакції?
hellojinjie

26

Якщо ви хочете уникнути перекриттів та з задоволенням користуєтесь фасадами, наступне робить речі приємними та чистими:

try {
    \DB::beginTransaction();

    $user = \Auth::user();
    $user->fill($request->all());
    $user->push();

    \DB::commit();

} catch (Throwable $e) {
    \DB::rollback();
}

Якщо будь-які оператори не вдаються, коміт ніколи не потрапить, і транзакція не оброблятиметься.


Якщо будь-які оператори не вдаються, наступні оператори не запускатимуться. Вам все одно потрібно явно відмовити транзакцію.
Джейсон

1
@ Джейсон Я оновив відповідь. Я двічі думав про те, чи повинен я, для більшості (усіх?) Механізмів баз даних, коли підключення припиняється, будь-які транзакційні запити, які не були скоєні, не будуть здійснені. Однак я погоджуюся з тим, що ви говорите, і, мабуть, найкраще бути чітким
Кріс,

18

Я впевнений, що ви не шукаєте рішення закриття, спробуйте це для більш компактного рішення

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

    DB::commit();
}catch(\Exception $e){
    DB::rollback();
}

10

З якоїсь причини досить важко знайти цю інформацію де-небудь, тому я вирішив розмістити її тут, оскільки моє видання, хоч і було пов’язане з красномовними транзакціями, саме змінило це.

Прочитавши ЦЮ відповідь на stackoverflow, я зрозумів, що мої таблиці бази даних використовують MyISAM замість InnoDB.

Щоб транзакції працювали на Laravel (або де-небудь ще, як здається), потрібно, щоб ваші таблиці використовували InnoDB

Чому?

Цитування документації щодо транзакцій MySQL та атомних операцій ( тут ):

Сервер MySQL (версія 3.23-max та всі версії 4.0 і вище) підтримує транзакції з механізмами зберігання транзакцій InnoDB та BDB. InnoDB забезпечує повну відповідність кислоті. Див. Розділ 14, Механізми зберігання. Інформацію про відмінності InnoDB від стандартного SQL щодо обробки помилок транзакцій див. У Розділі 14.2.11, “Обробка помилок InnoDB”.

Інші нетранзакційні механізми зберігання на MySQL Server (наприклад, MyISAM) дотримуються іншої парадигми цілісності даних, яка називається «атомні операції». З точки зору транзакцій, таблиці MyISAM ефективно завжди працюють в режимі autocommit = 1. Атомні операції часто пропонують порівнянну цілісність і високу продуктивність.

Оскільки сервер MySQL підтримує обидві парадигми, ви можете вирішити, чи найкраще ваші додатки обслуговуються швидкістю атомних операцій або використанням транзакційних функцій. Цей вибір можна зробити для кожного столу.


Це справедливо для DML і не завжди справедливо для DDL.
Євген Афанасьєв

4

У разі виникнення будь-якого винятку транзакція автоматично відкочується.

Формат транзакції Laravel Basic

    try{
    DB::beginTransaction();

    /* 
    * SQL operation one 
    * SQL operation two
    ..................     
    ..................     
    * SQL operation n */


    DB::commit();
   /* Transaction successful. */
}catch(\Exception $e){       

    DB::rollback();
    /* Transaction failed. */
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.