Beispiele für PHP + MySQL-Transaktionen

294

Ich habe wirklich kein normales Beispiel für eine PHP-Datei gefunden, in der MySQL-Transaktionen verwendet werden. Können Sie mir ein einfaches Beispiel dafür zeigen?

Und noch eine Frage. Ich habe bereits viel programmiert und keine Transaktionen verwendet. Kann ich eine PHP-Funktion oder etwas anderes hinzufügen header.php, wenn eine mysql_queryausfällt, dann fallen auch die anderen aus?


Ich glaube, ich habe es herausgefunden, stimmt es?:

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");
}
guten Abend
quelle
10
Sie können mysql_query("BEGIN");anstelle von Sequenz verwendenmysql_query("SET AUTOCOMMIT=0"); mysql_query("START TRANSACTION");
Kirzilla
75
Bitte verwenden Sie keine mysql_*Funktionen in neuem Code . Sie werden nicht mehr gewartet und sind offiziell veraltet . Sehen Sie die rote Box ? Erfahrenmehr über vorbereitete Anweisungen statt, und verwenden Sie PDO oder MySQLi - dieser Artikel wird Ihnen helfen, entscheidenwelche. Wenn Sie sich für PDO entscheiden, finden Sie hier ein gutes Tutorial .
Naftali aka Neal
6
Hat "mysql_query (" SET AUTOCOMMIT = 0 ");" Alle Verbindungen so einstellen, dass sie auf die Festschreibungsfunktion warten, oder nur auf die zugehörige Verbindung?
Hamid
1
@Neal, tatsächlich mysqlsterben, obwohl es veraltet ist, wird es für immer in PECL verfügbar sein.
Pacerier
2
@ Pacerier Dinge, die veraltet sind, "sterben" nicht. Sie werden offiziell für ältere Software gehalten, werden jedoch nicht mehr gewartet und von den empfohlenen Praktiken für neue Software ausgeschlossen. Die Tatsache bleibt, verwenden Sie nichtmysql
Taylorcressy

Antworten:

325

Die Idee, die ich normalerweise bei der Arbeit mit Transaktionen verwende, sieht folgendermaßen aus (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();
}


Beachten Sie, dass bei dieser Idee, wenn eine Abfrage fehlschlägt, eine Ausnahme ausgelöst werden muss:

  • PDO kann dies tun, je nachdem, wie Sie es konfigurieren
  • Andernfalls müssen Sie bei einer anderen API möglicherweise das Ergebnis der Funktion zum Ausführen einer Abfrage testen und selbst eine Ausnahme auslösen.


Leider gibt es keine Magie. Sie können nicht einfach irgendwo eine Anweisung platzieren und Transaktionen automatisch ausführen lassen: Sie müssen immer noch angeben, welche Gruppe von Abfragen in einer Transaktion ausgeführt werden muss.

Zum Beispiel sehr oft werden Sie ein paar Anfragen , die vor der Transaktion haben (vor dem begin) und ein anderes Paar von Abfragen nach der Transaktion (entweder nach commitoder rollback) , und Sie werden diese Abfragen ausgeführt wollen , egal was passiert ist (oder nicht) in die Transaktion.

Pascal MARTIN
quelle
35
Seien Sie vorsichtig, wenn Sie Vorgänge ausführen, bei denen andere als db-Ausnahmen auftreten können. In diesem Fall kann eine Ausnahme von einer Nicht-DB-Anweisung versehentlich zu einem Rollback führen (selbst wenn alle DB-Aufrufe erfolgreich sind). Normalerweise würden Sie denken, dass ein Rollback eine gute Idee ist, auch wenn der Fehler nicht auf der Datenbankseite lag, aber es gibt Zeiten, in denen Code von Drittanbietern / unkritischer Code nicht so wichtige Ausnahmen verursachen kann, und Sie möchten trotzdem fortfahren die Transaktion.
Halil Özgür
6
Was ist der $dbTyp hier? Mysqli?
Jake
3
@Jake In meiner Antwort finden Sie ein Beispiel, das mysqli verwendet (ähnlich wie Pascals Ansatz).
EleventyOne
2
Es kann leicht geändert werden, um PDOExceptionbei Bedarf Ausnahmewerte abzufangen und sogar zu überprüfen. us2.php.net/PDOException
Yamiko
1
$ db ist das PDO-Objekt (Verbindung). Ref: php.net/manual/en/pdo.connections.php
Fil
110

Ich glaube, ich habe es herausgefunden, stimmt es?:

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");
}
guten Abend
quelle
26
Autocommit muss nicht auf 0 gesetzt werden. Transaktionen funktionieren immer so.
BG-Code
2
@babonk - nicht sicher, ob dies bei InnoDB der Fall ist?
Buggedcom
6
Ich denke, sobald Sie eine Transaktion starten, funktioniert es, als ob AUTOCOMMIT = 0
bgcode
4
@babonk ist richtig. Sobald eine Transaktion gestartet wurde, wird AUTOCOMMIT = 0 implizit festgelegt. Nachdem die Transaktion entweder durch Festschreiben oder Rollback beendet wurde, setzt MySql den AUTOCOMMIT-Wert zurück, der vor dem Starten der Transaktion verwendet wurde. HINWEIS: Sie sollten AUTOCOMMIT NICHT auf 0 setzen, da Sie nach dem Festschreiben der Änderungen, wenn Sie eine weitere Zeile einfügen / aktualisieren möchten, diese explizit festschreiben sollten.
eroteev
4
Der Maschinenladen sollte InnoDB sein, nicht 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
quelle
Für eine breite und hochkarätige Frage wie diese wäre es großartig, wenn die Antworten dies auch widerspiegeln würden. Ihr Codebeispiel ist großartig, aber können Sie mehr ausarbeiten? Erklären Sie Transaktionen, warum, wann und wo? Verknüpfen Sie zum Schluss den Code mit Ihrer Erklärung.
Dennis Haarbrink
3
Willkommen bei StackOverflow. Bitte schreiben Sie immer einen beschreibenden Text zu Ihrer Antwort.
Adrian Heine
6
Entschuldigung, ich bin Anfänger, und mein schlechtes Englisch, es ist ein sehr einfaches Beispiel für Code - für Anfänger - commit () rollback () begin () in der Klasse DB (zum Beispiel), $ query - nicht einmal - vielleicht $ query0 $ query1 - dann chek sie - ich benutze diesen Code, das ist sehr leicht zu verstehen =)
Gedzberg Alex
20
Seine Kommentare machen das Beispiel ziemlich klar. Guter Code sollte keinen beschreibenden Text benötigen. Auch die Frage fragt nach einem einfachen Beispiel. Ich mag diese Antwort.
Keine
@GedzbergAlex für eine einzelne Abfrage ist keine Transaktion erforderlich, es wird lediglich die Transaktion verwirrt. Gibt es einen Grund, die Transaktion für eine einzelne Abfrage zu verwenden?
24ıɥʇɹɐʞ ouɐɯ
35

Da dies das erste Ergebnis bei Google für "php mysql transaction" ist, dachte ich, ich würde eine Antwort hinzufügen, die explizit zeigt, wie dies mit mysqli gemacht wird (wie der ursprüngliche Autor Beispiele wollte). Hier ist ein vereinfachtes Beispiel für Transaktionen mit 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   
}

Beachten Sie auch, dass PHP 5.5 eine neue Methode mysqli :: begin_transaction hat . Dies wurde jedoch noch nicht vom PHP-Team dokumentiert, und ich stecke immer noch in PHP 5.3 fest, daher kann ich keinen Kommentar dazu abgeben.

EleventyOne
quelle
2
In einem ähnlichen Zusammenhang habe ich gerade festgestellt, dass es bei der Arbeit mit InnoDB-Tabellen möglich ist, Tabellen zu sperren / zu entsperren, wenn der Ansatz autocomitt () für Transaktionen verwendet wird. Bei Verwendung des Ansatzes begin_transaction () ist dies jedoch NICHT möglich: MySQL Dokumentation
EleventyOne
+1 für detailliertes (und kommentiertes) Beispiel mit aktuellem MySQL-Code. Danke dafür. Und Ihr Punkt über Sperren / Transaktionen ist in der Tat sehr interessant.
a.real.human.being
1
Beeinflusst "Autocommit (FALSE)" eine andere Verbindung in derselben Datenbank / Tabelle? Ich meine, wenn wir zwei Seiten öffnen, setzt eine von ihnen ihre Verbindung auf "Autocommit (FALSE)", während die andere die Autocommit-Funktion verlassen hat, wartet sie auf die Commit-Funktion oder nicht. Ich möchte wissen, ob Autocommit ein Attribut für Verbindungen und nicht für Datenbank / Tabelle ist. Danke
Hamid
2
@Hamid wirkt $conn->autocommit(FALSE)sich im obigen Beispiel nur auf die einzelne Verbindung aus - es hat keine Auswirkungen auf andere Verbindungen zur Datenbank.
EleventyOne
1
HINWEIS: Anstelle von if (!result), sollte dies if (result === false)der Fall sein , wenn die Abfrage ein gültiges Ergebnis zurückgeben kann, das als falsch oder null ausgewertet wird.
ToolmakerSteve
10

Bitte überprüfen Sie, welche Speicher-Engine Sie verwenden. Wenn es sich um MyISAM handelt, Transaction('COMMIT','ROLLBACK')wird dies nicht unterstützt, da nur die InnoDB-Speicher-Engine und nicht MyISAM Transaktionen unterstützt.

Dinesh
quelle
7

Bei Verwendung der PDO-Verbindung:

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

Ich verwende häufig den folgenden Code für das Transaktionsmanagement:

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
    }
}

Anwendungsbeispiel:

transaction(function()
{
    global $pdo;

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

Auf diese Weise wird der Transaktionsverwaltungscode nicht im gesamten Projekt dupliziert. Das ist eine gute Sache, denn nach anderen PDO-bewerteten Antworten in diesem Thread zu urteilen, ist es leicht, Fehler darin zu machen. Die häufigsten sind das Vergessen, die Ausnahme erneut auszulösen und die Transaktion innerhalb des tryBlocks zu starten .

Danila Piatov
quelle
5

Ich habe eine Funktion erstellt, um einen Vektor von Abfragen abzurufen und eine Transaktion durchzuführen. Vielleicht findet es jemand nützlich:

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
quelle
3

Ich hatte das, bin mir aber nicht sicher, ob das richtig ist. Könnte das auch ausprobieren.

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");
}

Idee von hier: http://www.phpknowhow.com/mysql/transactions/

Nodeffect
quelle
Nicht korrekter Code. trigger_error gibt true zurück (es sei denn, Sie haben Ihren Aufruf vermasselt), sodass $ result immer true ist, sodass dieser Code jede fehlgeschlagene Abfrage übersieht und immer versucht, ein Commit durchzuführen. Ebenso beunruhigend ist, dass Sie das alte veraltete verwenden mysql_query, anstatt es zu verwenden mysqli, obwohl Sie auf ein Tutorial verlinken, das verwendet wird mysqli. IMHO sollten Sie entweder dieses schlechte Beispiel löschen oder es ersetzen, um Code zu verwenden, wie er im phpknowhow-Tutorial geschrieben wurde.
ToolmakerSteve
2

Ein weiteres Beispiel für einen prozeduralen Stil mit mysqli_multi_query, angenommen, $queryist mit durch Semikolons getrennten Anweisungen gefüllt.

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);

quelle
1
Etwas seltsamer Code, aber zumindest deutet er auf die Verwendung von hin mysqli_multi_query. Jeder, der daran interessiert ist, sollte an anderer Stelle nach einem saubereren Beispiel suchen.
ToolmakerSteve