web-dev-qa-db-fra.com

PHP + exemples de transactions MySQL

Je n'ai pas vraiment trouvé d'exemple normal de fichier PHP dans lequel les transactions MySQL sont utilisées. Pouvez-vous me montrer un exemple simple de cela?

Et une autre question. J'ai déjà beaucoup programmé et n'ai pas utilisé de transactions. Puis-je mettre une fonction PHP ou quelque chose dans header.php que si l'un mysql_query échoue, les autres échouent aussi?


Je pense l'avoir compris, est-ce vrai ?:

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

L'idée que j'utilise généralement lorsque je travaille avec des transactions ressemble à ceci (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();
}
_


Notons qu'avec cette idée, si une requête échoue, une exception doit être levée:

  • PDO peut le faire, selon la manière dont vous le configurez.
  • sinon, avec une autre API, vous devrez peut-être tester le résultat de la fonction utilisée pour exécuter une requête et lever une exception vous-même.


Malheureusement, aucune magie n’est en cause. Vous ne pouvez pas simplement placer une instruction quelque part et effectuer des transactions automatiquement: vous devez encore spécifier quel groupe de requêtes doit être exécuté dans une transaction.

Par exemple, vous aurez souvent plusieurs requêtes avant la transaction (avant la begin) et une autre paire de requêtes après la transaction (après soit commit ou rollback) et vous souhaiterez que ces requêtes soient exécutées quoi qu’il se soit passé (ou pas) dans la transaction.

316
Pascal MARTIN

Je pense l'avoir compris, est-ce vrai ?:

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

?>
39
Gedzberg Alex

Comme c'est le premier résultat sur Google pour "php mysql transaction", j'ai pensé ajouter une réponse expliquant explicitement comment faire cela avec mysqli (comme l'auteur original en voulait des exemples). Voici un exemple simplifié de transactions avec 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   
}

Aussi, gardez à l'esprit que PHP 5.5 a une nouvelle méthode mysqli :: begin_transaction . Cependant, ceci n'a pas encore été documenté par l'équipe PHP, et je suis toujours bloqué dans PHP 5.3, je ne peux donc pas en parler.

35
EleventyOne

Veuillez vérifier quel moteur de stockage vous utilisez. S'il s'agit de MyISAM, alors Transaction('COMMIT','ROLLBACK') ne sera pas pris en charge car seul le moteur de stockage InnoDB, et non MyISAM, prend en charge les transactions.

8
dinesh

Lors de l'utilisation d'une connexion PDO:

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

J'utilise souvent le code suivant pour la gestion des transactions:

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

Exemple d'utilisation:

transaction(function()
{
    global $pdo;

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

Ainsi, le code de gestion des transactions n'est pas dupliqué dans le projet. Ce qui est une bonne chose, car, à en juger par les autres réponses développées dans ce fil, il est facile de s'y tromper. Les plus courants sont ceux qui oublient de redéfinir l'exception et qui démarrent la transaction dans le bloc try.

5
Danila Piatov

J'ai créé une fonction pour obtenir un vecteur de requêtes et effectuer une transaction, peut-être que quelqu'un le trouvera utile:

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;
        }
    }
5
Marco

J'ai eu cela, mais pas sûr si c'est correct. Pourrait essayer ceci aussi.

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

Idée d'ici: http://www.phpknowhow.com/mysql/transactions/

3
nodeffect

Un autre exemple de style procédural avec mysqli_multi_query suppose que $query est rempli d'instructions séparées par des points-virgules.

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