Laravel Eloquent ORM ธุรกรรม


97

Eloquent ORM ค่อนข้างดีแม้ว่าฉันจะสงสัยว่ามีวิธีง่ายๆในการตั้งค่าธุรกรรม MySQL โดยใช้ innoDB แบบเดียวกับ PDO หรือไม่หรือฉันจะต้องขยาย ORM เพื่อให้เป็นไปได้

คำตอบ:


167

คุณสามารถทำได้:

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

ทุกสิ่งที่อยู่ใน Closure จะดำเนินการภายในธุรกรรม หากมีข้อยกเว้นเกิดขึ้นจะย้อนกลับโดยอัตโนมัติ


1
ฉันสามารถเรียกแบบสอบถามในชั้นเรียนได้หรือไม่? มันจะทำงาน?
Rafael Soufraz

น่าเศร้าที่มันใช้งานไม่ได้สำหรับฉันถ้าฉันสร้างอินสแตนซ์ของโมเดลต่างๆที่กำลังจัดเก็บบันทึกในวิธีการที่เกี่ยวข้อง
Volatil3

หากฉันตรวจพบข้อยกเว้นในธุรกรรมของฉัน (สำหรับการสร้างข้อความแสดงข้อผิดพลาด ฯลฯ ) ฉันจำเป็นต้องปล่อยข้อยกเว้นอีกครั้งเพื่อให้การย้อนกลับเกิดขึ้นหรือไม่
alexw

3
คำตอบที่ดี แต่มีสองสิ่งที่ทำให้ฉันเข้าใจ: 1. คุณต้องเพิ่ม "use DB;" เพื่อทำสิ่งนี้เช่นที่ด้านบนของไฟล์โมเดลของคุณ 2 ซึ่งแตกต่างจาก JS คุณจะไม่สามารถเข้าถึงตัวแปรโลคัลในขอบเขตหลักได้เว้นแต่คุณจะส่งผ่านไปอย่างชัดเจนคุณต้องเพิ่มโครงสร้าง "ใช้" ด้วยเหตุนี้ ... DB :: transaction (function () use ($ user) {... stuffs referencing $ user ... });
Polsonby

Discussed in more detail hereลิงค์ตายแล้ว
tomloprod

102

หากคุณไม่ชอบฟังก์ชั่นที่ไม่ระบุตัวตน:

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()นั่นจะเป็นการทำความสะอาดเล็กน้อย
Flori

2
โปรดอัปเดตเพื่อใช้ข้อเสนอแนะของ @Flori มันสะอาดกว่า นอกจากนี้การเลื่อนคำตอบใหม่ขึ้นจะทำให้คำตอบของคุณไม่สับสน ฉันใช้วิธีแรกก่อนที่จะกลับมาเป็นวิธีที่สอง
หนาวจัด

สำหรับ Laravel เวอร์ชันเก่าคุณอาจต้องใช้:DB::connection()->getPdo()->beginTransaction();
แทน

โดยส่วนตัวแล้วฉันคิดว่าDB::transactionด้วยการโทรกลับนั้นสะอาดกว่า แต่ข้อเสียคือหากคุณต้องการระบุตัวจัดการที่แตกต่างกันสำหรับข้อยกเว้นที่แตกต่างกันคุณจะต้องกลับไปใช้เทคนิคการลอง / จับ
OzzyTheGiant

34

หากคุณต้องการใช้ 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

30

หากคุณต้องการหลีกเลี่ยงการปิดและยินดีที่จะใช้อาคารสิ่งต่อไปนี้จะช่วยให้สิ่งต่างๆดีและสะอาด:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

หากคำสั่งใด ๆ ล้มเหลวการกระทำจะไม่ถูกกระทบและธุรกรรมจะไม่ดำเนินการ


หากคำสั่งใด ๆ ล้มเหลวคำสั่งที่ตามมาจะไม่ทำงาน คุณยังคงต้องย้อนกลับธุรกรรมอย่างชัดเจน
Jason

1
@ Jason ฉันได้อัปเดตคำตอบแล้ว ฉันอยู่ในสองจิตสองใจเกี่ยวกับว่าฉันควรหรือไม่สำหรับเอ็นจินฐานข้อมูลส่วนใหญ่ (ทั้งหมด?) เมื่อการเชื่อมต่อถูกยุติการสอบถามธุรกรรมใด ๆ ที่ไม่ได้กระทำจะไม่ถูกดำเนินการ อย่างไรก็ตามฉันเห็นด้วยกับสิ่งที่คุณพูดและอาจจะดีที่สุดที่จะพูดอย่างชัดเจน
คริส

19

ฉันแน่ใจว่าคุณไม่ได้มองหาวิธีการปิดลองวิธีนี้สำหรับโซลูชันที่กะทัดรัดกว่านี้

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

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

10

ด้วยเหตุผลบางประการมันค่อนข้างยากที่จะค้นหาข้อมูลนี้ได้ทุกที่ดังนั้นฉันจึงตัดสินใจโพสต์ที่นี่เนื่องจากปัญหาของฉันในขณะที่เกี่ยวข้องกับการทำธุรกรรมที่มีประสิทธิภาพกำลังเปลี่ยนแปลงสิ่งนี้อย่างสิ้นเชิง

หลังจากอ่านคำตอบสแตกโอเวอร์โฟลว์นี้ฉันรู้ว่าตารางฐานข้อมูลของฉันใช้ MyISAM แทน InnoDB

สำหรับการทำธุรกรรมบน Laravel (หรือที่อื่น ๆ ตามที่ดูเหมือน) จำเป็นต้องตั้งค่าตารางของคุณให้ใช้ InnoDB

ทำไม?

อ้างถึงเอกสารธุรกรรม MySQL และการดำเนินการปรมาณู ( ที่นี่ ):

MySQL Server (เวอร์ชัน 3.23-max และเวอร์ชัน 4.0 ขึ้นไปทั้งหมด) รองรับการทำธุรกรรมกับเอ็นจินหน่วยเก็บข้อมูลธุรกรรม InnoDB และ BDB InnoDB ให้การปฏิบัติตาม ACID อย่างสมบูรณ์ โปรดดูบทที่ 14 เครื่องมือจัดเก็บข้อมูล สำหรับข้อมูลเกี่ยวกับความแตกต่างของ InnoDB จาก SQL มาตรฐานที่เกี่ยวข้องกับการรักษาข้อผิดพลาดในการทำธุรกรรมโปรดดูหัวข้อ 14.2.11“ การจัดการข้อผิดพลาด InnoDB”

เอ็นจินการจัดเก็บข้อมูลแบบไม่ทำธุรกรรมอื่น ๆ ใน MySQL Server (เช่น MyISAM) เป็นไปตามกระบวนทัศน์ที่แตกต่างกันสำหรับความสมบูรณ์ของข้อมูลที่เรียกว่า "การดำเนินการของอะตอม" ในเงื่อนไขการทำธุรกรรมตาราง MyISAM จะทำงานในโหมด autocommit = 1 อย่างมีประสิทธิภาพเสมอ การดำเนินการของอะตอมมักให้ความสมบูรณ์เทียบเท่ากับประสิทธิภาพที่สูงกว่า

เนื่องจาก MySQL Server รองรับทั้งสองกระบวนทัศน์คุณจึงสามารถตัดสินใจได้ว่าแอปพลิเคชันของคุณจะได้รับการตอบสนองที่ดีที่สุดด้วยความเร็วของการดำเนินการของอะตอมหรือการใช้คุณสมบัติการทำธุรกรรม ตัวเลือกนี้สามารถทำได้แบบต่อโต๊ะ


นี่เป็นความจริงสำหรับ DML และไม่เป็นความจริงสำหรับ DDL เสมอไป
Yevgeniy Afanasyev

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. */
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.