Contoh transaksi PHP + MySQL

294

Saya benar-benar belum menemukan contoh file PHP normal di mana transaksi MySQL sedang digunakan. Dapatkah Anda menunjukkan kepada saya contoh sederhana tentang itu?

Dan satu pertanyaan lagi. Saya sudah melakukan banyak pemrograman dan tidak menggunakan transaksi. Bisakah saya memasukkan fungsi PHP atau sesuatu header.phpjika ada yang mysql_querygagal, maka yang lain juga gagal?


Saya pikir saya sudah menemukan jawabannya, benarkah ?:

mysql_query("SET AUTOCOMMIT=0");
mysql_query("START TRANSACTION");

$a1 = mysql_query("INSERT INTO rarara (l_id) VALUES('1')");
$a2 = mysql_query("INSERT INTO rarara (l_id) VALUES('2')");

if ($a1 and $a2) {
    mysql_query("COMMIT");
} else {        
    mysql_query("ROLLBACK");
}
Selamat malam
sumber
10
Anda dapat menggunakan mysql_query("BEGIN");sebagai ganti urutanmysql_query("SET AUTOCOMMIT=0"); mysql_query("START TRANSACTION");
Kirzilla
75
Tolong, jangan gunakan mysql_*fungsi dalam kode baru . Mereka tidak lagi dipertahankan dan secara resmi ditinggalkan . Lihat kotak merah ? Pelajari tentang pernyataan yang disiapkan , dan gunakan PDO atau MySQLi - artikel ini akan membantu Anda memutuskan yang mana. Jika Anda memilih PDO, berikut adalah tutorial yang bagus .
Naftali aka Neal
6
Apakah "mysql_query (" SET AUTOCOMMIT = 0 ");" atur semua koneksi untuk menunggu fungsi komit atau hanya untuk koneksi yang terkait?
Hamid
1
@Neal, Sebenarnya mysqlwun mati meskipun sudah usang, itu akan tersedia di PECL selamanya.
Pacerier
2
@Pacerier Hal-hal yang sudah usang jangan "mati". Mereka diadakan secara resmi untuk perangkat lunak lama tetapi tidak lagi dipertahankan dan dihilangkan dari praktik yang disarankan untuk perangkat lunak baru. Faktanya tetap, jangan gunakanmysql
taylorcressy

Jawaban:

325

Ide yang biasanya saya gunakan ketika bekerja dengan transaksi terlihat seperti ini (semi-pseudo-code) :

try {
    // First of all, let's begin a transaction
    $db->beginTransaction();

    // A set of queries; if one fails, an exception should be thrown
    $db->query('first query');
    $db->query('second query');
    $db->query('third query');

    // If we arrive here, it means that no exception was thrown
    // i.e. no query has failed, and we can commit the transaction
    $db->commit();
} catch (Exception $e) {
    // An exception has been thrown
    // We must rollback the transaction
    $db->rollback();
}


Perhatikan bahwa, dengan gagasan ini, jika kueri gagal, Pengecualian harus dilemparkan:

  • PDO dapat melakukannya, tergantung pada bagaimana Anda mengkonfigurasinya
  • selain itu, dengan beberapa API lain, Anda mungkin harus menguji hasil fungsi yang digunakan untuk menjalankan kueri, dan melemparkan sendiri pengecualian.


Sayangnya, tidak ada sihir yang terlibat. Anda tidak bisa hanya menempatkan instruksi di suatu tempat dan melakukan transaksi secara otomatis: Anda masih harus menentukan kelompok kueri mana yang harus dieksekusi dalam transaksi.

Misalnya, cukup sering Anda akan memiliki beberapa pertanyaan sebelum transaksi (sebelum begin) dan beberapa kueri lain setelah transaksi (setelah salah satu commitatau rollback) dan Anda ingin kueri tersebut dieksekusi tidak peduli apa yang terjadi (atau tidak) di transaksi itu.

Pascal MARTIN
sumber
35
Hati-hati jika Anda melakukan operasi yang dapat menimbulkan pengecualian selain dari yang db. Jika demikian, pengecualian dari pernyataan non-db dapat menyebabkan rollback secara tidak sengaja (bahkan jika semua panggilan db berhasil). Biasanya, Anda akan berpikir memutar kembali adalah ide yang baik bahkan jika kesalahannya tidak di sisi db, tetapi ada kalanya pihak ke-3 / kode tidak kritis dapat menyebabkan pengecualian yang tidak terlalu penting, dan Anda masih ingin melanjutkan transaksi itu.
Halil Özgür
6
Apa $dbjenisnya di sini? mysqli?
Jake
3
@Jake Lihat jawaban saya untuk contoh yang menggunakan mysqli (mirip dengan pendekatan Pascal).
EleventyOne
2
dapat dengan mudah dimodifikasi untuk menangkap PDOExceptiondan bahkan memeriksa nilai pengecualian jika diperlukan. us2.php.net/PDOException
Yamiko
1
$ db adalah objek PDO (koneksi). Ref: php.net/manual/en/pdo.connections.php
Fil
110

Saya pikir saya sudah menemukan jawabannya, benarkah ?:

mysql_query("START TRANSACTION");

$a1 = mysql_query("INSERT INTO rarara (l_id) VALUES('1')");
$a2 = mysql_query("INSERT INTO rarara (l_id) VALUES('2')");

if ($a1 and $a2) {
    mysql_query("COMMIT");
} else {        
    mysql_query("ROLLBACK");
}
Selamat malam
sumber
26
tidak perlu mengatur autocommit = 0. transaksi selalu bekerja seperti itu.
bgcode
2
@ babonk - tidak yakin ini halnya dengan InnoDB?
buggedcom
6
Saya pikir begitu Anda memulai transaksi berfungsi seolah-olah AUTOCOMMIT = 0
bgcode
4
@babonk benar. Setelah transaksi dimulai, AUTOCOMMIT = 0 ditetapkan secara implisit dan setelah transaksi berakhir dengan komit atau rollback, MySql menetapkan kembali nilai AUTOCOMMIT yang digunakan sebelum memulai transaksi. CATATAN: Anda TIDAK boleh mengatur AUTOCOMMIT = 0, karena setelah melakukan perubahan jika Anda memutuskan untuk memasukkan / memperbarui baris lain Anda harus melakukan itu secara eksplisit.
eroteev
4
Toko mesin harus InnoDB, bukan MyISAM!
javad
39
<?php

// trans.php
function begin(){
    mysql_query("BEGIN");
}

function commit(){
    mysql_query("COMMIT");
}

function rollback(){
    mysql_query("ROLLBACK");
}

mysql_connect("localhost","Dude1", "SuperSecret") or die(mysql_error());

mysql_select_db("bedrock") or die(mysql_error());

$query = "INSERT INTO employee (ssn,name,phone) values ('123-45-6789','Matt','1-800-555-1212')";

begin(); // transaction begins

$result = mysql_query($query);

if(!$result){
    rollback(); // transaction rolls back
    echo "transaction rolled back";
    exit;
}else{
    commit(); // transaction is committed
    echo "Database transaction was successful";
}

?>
Gedzberg Alex
sumber
Untuk pertanyaan yang luas dan profil tinggi seperti ini, alangkah baiknya jika jawabannya juga mencerminkan hal itu. Sampel kode Anda hebat, tetapi bisakah Anda menjelaskan lebih lanjut? Jelaskan tentang transaksi, mengapa, kapan dan di mana? Akhirnya, tautkan kode dengan penjelasan Anda.
Dennis Haarbrink
3
Selamat datang di StackOverflow. Harap selalu menulis beberapa teks penjelasan untuk jawaban Anda.
Adrian Heine
6
maaf im pemula, dan bahasa Inggris saya yang buruk, sangat mudah memeriksa kode - untuk pemula - commit () rollback () mulai () dimasukkan ke dalam kelas DB (misalnya), $ query - tidak sekali - mungkin $ query0 $ query1 - lalu chek mereka - saya menggunakan kode ini, ini sangat mudah dimengerti =)
Gedzberg Alex
20
Komentarnya membuat contoh cukup jelas. Kode yang baik seharusnya tidak perlu menggambarkan teks. Juga pertanyaannya menanyakan contoh sederhana. Saya suka jawaban ini.
Tidak ada
@GedzbergAlex untuk permintaan tunggal tidak perlu transaksi, cukup membingungkan tentang transaksi, Apakah ada alasan untuk menggunakan transaksi untuk permintaan tunggal?
ʞɔıɥʇɹɐʞ ouɐɯ
35

Karena ini adalah hasil pertama di google untuk "transaksi php mysql", saya pikir saya akan menambahkan jawaban yang secara eksplisit menunjukkan bagaimana melakukan ini dengan mysqli (seperti yang diinginkan penulis asli contoh). Berikut adalah contoh transaksi yang disederhanakan dengan PHP / mysqli:

// let's pretend that a user wants to create a new "group". we will do so
// while at the same time creating a "membership" for the group which
// consists solely of the user themselves (at first). accordingly, the group
// and membership records should be created together, or not at all.
// this sounds like a job for: TRANSACTIONS! (*cue music*)

$group_name = "The Thursday Thumpers";
$member_name = "EleventyOne";
$conn = new mysqli($db_host,$db_user,$db_passwd,$db_name); // error-check this

// note: this is meant for InnoDB tables. won't work with MyISAM tables.

try {

    $conn->autocommit(FALSE); // i.e., start transaction

    // assume that the TABLE groups has an auto_increment id field
    $query = "INSERT INTO groups (name) ";
    $query .= "VALUES ('$group_name')";
    $result = $conn->query($query);
    if ( !$result ) {
        $result->free();
        throw new Exception($conn->error);
    }

    $group_id = $conn->insert_id; // last auto_inc id from *this* connection

    $query = "INSERT INTO group_membership (group_id,name) ";
    $query .= "VALUES ('$group_id','$member_name')";
    $result = $conn->query($query);
    if ( !$result ) {
        $result->free();
        throw new Exception($conn->error);
    }

    // our SQL queries have been successful. commit them
    // and go back to non-transaction mode.

    $conn->commit();
    $conn->autocommit(TRUE); // i.e., end transaction
}
catch ( Exception $e ) {

    // before rolling back the transaction, you'd want
    // to make sure that the exception was db-related
    $conn->rollback(); 
    $conn->autocommit(TRUE); // i.e., end transaction   
}

Juga, perlu diingat bahwa PHP 5.5 memiliki metode baru mysqli :: begin_transaction . Namun, ini belum didokumentasikan oleh tim PHP, dan saya masih terjebak di PHP 5.3, jadi saya tidak bisa mengomentarinya.

EleventyOne
sumber
2
Pada catatan terkait, saya baru saja menemukan bahwa jika Anda bekerja dengan tabel InnoDB, itu mungkin untuk mengunci / membuka kunci tabel ketika menggunakan pendekatan autocomitt () untuk transaksi, tetapi TIDAK mungkin ketika menggunakan pendekatan begin_transaction (): MySQL dokumentasi
EleventyOne
+1 untuk contoh terperinci (dan dikomentari) dengan kode mysqli aktual. Terima kasih untuk ini. Dan poin Anda tentang penguncian / transaksi memang sangat menarik.
a.real.human.being
1
Apakah "autocommit (FALSE)" akan memengaruhi koneksi lain di database / tabel yang sama? Maksud saya jika kita membuka dua halaman yang salah satu dari mereka mengatur koneksinya menjadi "autocommit (FALSE)" tetapi yang lain meninggalkan fungsi autocommit, apakah ia menunggu fungsi commit atau tidak. Saya ingin tahu apakah autocommit adalah atribut untuk koneksi dan bukan untuk database / tabel. Terima kasih
Hamid
2
@ Hamid $conn->autocommit(FALSE), dalam contoh di atas, hanya mempengaruhi koneksi individu - tidak berpengaruh pada koneksi lain ke database.
EleventyOne
1
CATATAN: alih-alih if (!result), harus dilakukan if (result === false), jika kueri mampu mengembalikan hasil yang valid yang akan dievaluasi menjadi false atau nol.
ToolmakerSteve
10

Silakan periksa mesin penyimpanan mana yang Anda gunakan. Jika itu adalah MyISAM, maka Transaction('COMMIT','ROLLBACK')tidak akan didukung karena hanya mesin penyimpanan InnoDB, bukan MyISAM, yang mendukung transaksi.

dinesh
sumber
7

Saat menggunakan koneksi PDO:

$pdo = new PDO('mysql:host=localhost;dbname=mydb;charset=utf8', $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // this is important
]);

Saya sering menggunakan kode berikut untuk manajemen transaksi:

function transaction(Closure $callback)
{
    global $pdo; // let's assume our PDO connection is in a global var

    // start the transaction outside of the try block, because
    // you don't want to rollback a transaction that failed to start
    $pdo->beginTransaction(); 
    try
    {
        $callback();
        $pdo->commit(); 
    }
    catch (Exception $e) // it's better to replace this with Throwable on PHP 7+
    {
        $pdo->rollBack();
        throw $e; // we still have to complain about the exception
    }
}

Contoh penggunaan:

transaction(function()
{
    global $pdo;

    $pdo->query('first query');
    $pdo->query('second query');
    $pdo->query('third query');
});

Dengan cara ini kode manajemen transaksi tidak digandakan di seluruh proyek. Yang merupakan hal yang baik, karena, kalau dilihat dari jawaban-jawaban lain yang direplikasi oleh PDO di utas ini, mudah untuk membuat kesalahan di dalamnya. Yang paling umum adalah lupa untuk menyusun kembali pengecualian dan memulai transaksi di dalam tryblok.

Danila Piatov
sumber
5

Saya membuat fungsi untuk mendapatkan vektor kueri dan melakukan transaksi, mungkin seseorang akan menemukannya berguna:

function transaction ($con, $Q){
        mysqli_query($con, "START TRANSACTION");

        for ($i = 0; $i < count ($Q); $i++){
            if (!mysqli_query ($con, $Q[$i])){
                echo 'Error! Info: <' . mysqli_error ($con) . '> Query: <' . $Q[$i] . '>';
                break;
            }   
        }

        if ($i == count ($Q)){
            mysqli_query($con, "COMMIT");
            return 1;
        }
        else {
            mysqli_query($con, "ROLLBACK");
            return 0;
        }
    }
Marco
sumber
3

Saya punya ini, tetapi tidak yakin apakah ini benar. Bisa coba ini juga.

mysql_query("START TRANSACTION");
$flag = true;
$query = "INSERT INTO testing (myid) VALUES ('test')";

$query2 = "INSERT INTO testing2 (myid2) VALUES ('test2')";

$result = mysql_query($query) or trigger_error(mysql_error(), E_USER_ERROR);
if (!$result) {
$flag = false;
}

$result = mysql_query($query2) or trigger_error(mysql_error(), E_USER_ERROR);
if (!$result) {
$flag = false;
}

if ($flag) {
mysql_query("COMMIT");
} else {        
mysql_query("ROLLBACK");
}

Ide dari sini: http://www.phpknowhow.com/mysql/transactions/

efek simpul
sumber
Bukan kode yang benar. trigger_error akan mengembalikan true (kecuali Anda telah mengacaukan panggilan Anda), jadi $ result akan selalu benar, sehingga kode ini akan kehilangan setiap kueri yang gagal, dan selalu berusaha untuk melakukan. Sama-sama meresahkan, Anda menggunakan yang lama tidak digunakan mysql_query, alih-alih menggunakan mysqli, meskipun Anda menautkan ke tutorial yang menggunakan mysqli. IMHO, Anda harus menghapus contoh buruk ini, atau menggantinya dengan menggunakan kode seperti yang ditulis dalam tutorial phpknowhow.
ToolmakerSteve
2

Satu lagi contoh gaya prosedural dengan mysqli_multi_query, diasumsikan $querydiisi dengan pernyataan yang dipisahkan dengan titik koma.

mysqli_begin_transaction ($link);

for (mysqli_multi_query ($link, $query);
    mysqli_more_results ($link);
    mysqli_next_result ($link) );

! mysqli_errno ($link) ?
    mysqli_commit ($link) : mysqli_rollback ($link);

sumber
1
Agak aneh kode, tapi setidaknya itu menyarankan penggunaan mysqli_multi_query. Siapa pun yang tertarik dengan itu harus mencari di tempat lain misalnya bersih.
ToolmakerSteve