Transaksi ORM Laravel Eloquent


96

ORM yang fasih cukup bagus, meskipun saya bertanya-tanya apakah ada cara mudah untuk mengatur transaksi MySQL menggunakan innoDB dengan cara yang sama seperti PDO, atau apakah saya harus memperluas ORM untuk memungkinkannya?

Jawaban:


165

Kamu bisa melakukan ini:

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

Segala sesuatu di dalam Penutupan dijalankan dalam transaksi. Jika pengecualian terjadi, ia akan melakukan rollback secara otomatis.


1
Di dalam closure saya bisa memanggil query di kelas? Ini akan berhasil?
Rafael Soufraz

Sayangnya itu tidak berfungsi untuk saya jika saya membuat contoh model berbeda yang menyimpan catatan dalam metode mereka sendiri yang relevan.
Volatil3

Jika saya menemukan pengecualian di dalam transaksi saya (untuk menghasilkan pesan kesalahan, dll.), Apakah saya perlu mengeluarkan kembali pengecualian agar rollback terjadi?
alexw

3
Jawaban yang bagus tetapi beberapa hal menarik perhatian saya: 1. Anda perlu menambahkan "use DB;" untuk melakukan ini misalnya di bagian atas file model Anda 2. Tidak seperti JS, Anda tidak mendapatkan akses ke variabel lokal dalam lingkup induk kecuali Anda secara eksplisit meneruskannya, Anda perlu menambahkan konstruksi "gunakan" dengan demikian ... DB :: transaction (function () use ($ user) {... barang yang merujuk $ user ...});
Polsonby

Discussed in more detail herelink sudah mati.
tomloprod

100

Jika Anda tidak menyukai fungsi anonim:

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

Pembaruan : Untuk laravel 4, pdoobjeknya tidak lagi publik jadi:

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

15
Anda juga dapat menggunakan metode pintasan DB::beginTransaction()& DB::commit()& DB::rollback(). Itu akan sedikit lebih bersih.
Flori

2
Perbarui untuk menggunakan saran @Flori. Itu lebih bersih. Selain itu, memindahkan jawaban baru ke atas akan membuat jawaban Anda tidak terlalu membingungkan. Saya menggunakan metode pertama sebelum kembali untuk yang kedua.
frostymarvelous

Untuk versi Laravel yang lebih lama, Anda mungkin memerlukan:DB::connection()->getPdo()->beginTransaction();
sebagai gantinya

Saya pribadi berpikir DB::transactiondengan callback bahkan lebih bersih tetapi kekurangannya adalah jika Anda perlu menentukan penangan yang berbeda untuk pengecualian yang berbeda, Anda harus kembali untuk mencoba / menangkap teknik
OzzyTheGiant

33

Jika Anda ingin menggunakan Eloquent, Anda juga dapat menggunakan ini

Ini hanya contoh kode dari proyek saya

        /* 
         * 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();
        });

The question->idekspresi di balik transaksi kembali nol.
Christos Papoulas

@ChristosPapoulas maksud Anda, kami tidak bisa mendapatkan auto increment id dalam transaksi?
hellojinjie

27

Jika Anda ingin menghindari penutupan, dan senang menggunakan fasad, hal-hal berikut ini menjaga agar tetap bagus dan bersih:

try {
    \DB::beginTransaction();

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

    \DB::commit();

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

Jika ada pernyataan yang gagal, komit tidak akan pernah berhasil, dan transaksi tidak akan diproses.


Jika ada pernyataan yang gagal, pernyataan berikutnya tidak akan berjalan. Anda masih perlu membatalkan transaksi secara eksplisit.
Jason

1
@Jason saya telah memperbarui jawabannya. Saya memiliki dua pikiran tentang jika saya harus, untuk sebagian besar (semua?) Mesin database, ketika koneksi diakhiri, setiap pertanyaan transaksional yang tidak dilakukan tidak akan dilakukan. Namun, saya setuju dengan apa yang Anda katakan, dan mungkin yang terbaik adalah secara eksplisit
Chris

19

Saya yakin Anda tidak mencari solusi penutupan, coba ini untuk solusi yang lebih ringkas

 try{
    DB::beginTransaction();

    /*
     * Your DB code
     * */

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

10

Untuk beberapa alasan cukup sulit untuk menemukan informasi ini di mana pun, jadi saya memutuskan untuk mempostingnya di sini, karena masalah saya, sementara terkait dengan transaksi Eloquent, benar-benar mengubah ini.

Setelah membaca jawaban stackoverflow INI , saya menyadari tabel database saya menggunakan MyISAM, bukan InnoDB.

Agar transaksi dapat berfungsi di Laravel (atau di mana pun yang terlihat), tabel Anda harus diatur untuk menggunakan InnoDB

Mengapa?

Mengutip dokumen Transaksi MySQL dan Operasi Atom (di sini ):

MySQL Server (versi 3.23-max dan semua versi 4.0 dan yang lebih baru) mendukung transaksi dengan mesin penyimpanan transaksional InnoDB dan BDB. InnoDB memberikan kepatuhan ACID penuh. Lihat Bab 14, Mesin Penyimpanan. Untuk informasi tentang perbedaan InnoDB dari SQL standar terkait dengan penanganan kesalahan transaksi, lihat Bagian 14.2.11, “Penanganan Kesalahan InnoDB”.

Mesin penyimpanan nontransaksional lainnya di Server MySQL (seperti MyISAM) mengikuti paradigma berbeda untuk integritas data yang disebut "operasi atom". Dalam istilah transaksional, tabel MyISAM secara efektif selalu beroperasi dalam mode autocommit = 1. Operasi atom sering menawarkan integritas yang sebanding dengan kinerja yang lebih tinggi.

Karena Server MySQL mendukung kedua paradigma tersebut, Anda dapat memutuskan apakah aplikasi Anda paling baik dilayani oleh kecepatan operasi atom atau penggunaan fitur transaksional. Pilihan ini dapat dibuat per tabel.


Ini benar untuk DML, dan tidak selalu benar untuk DDL.
Yevgeniy Afanasyev

4

Jika terjadi pengecualian, transaksi akan dibatalkan secara otomatis.

Format transaksi 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. */
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.