Exemples de transactions PHP + MySQL
Je n'ai vraiment pas trouvé d'exemple normal de fichier PHP où les transactions MySQL sont utilisées. Pouvez-vous me montrer un exemple simple de cela?
Et encore une question. J'ai déjà fait beaucoup de programmation et je n'ai pas utilisé de transactions. Puis-je mettre une fonction PHP ou quelque chose dans {[1] } que si l'un mysql_query
échoue, alors les autres échouent aussi?
Je pense que je l'ai compris, Est-ce juste?:
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");
}
9 réponses
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();
}
Notez que, avec cette idée, si une requête échoue, une Exception doit être levée:
- PDO peut le faire, en fonction de la façon dont vous le configurez
- Voir
PDO::setAttribute
- et
PDO::ATTR_ERRMODE
etPDO::ERRMODE_EXCEPTION
- Voir
- 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 lancer un exception vous-même.
Malheureusement, il n'y a pas de magie impliquée. Vous ne pouvez pas simplement mettre une instruction quelque part et faire effectuer des transactions automatiquement: vous devez toujours spécifier quel groupe de requêtes doit être exécuté dans une transaction.
Par exemple, assez souvent, vous aurez quelques requêtes avant la transaction (avant le begin
) et un autre couple de requêtes après la transaction (après commit
ou rollback
) et tu en voudras requêtes exécutées peu importe ce qui s'est passé (ou non) dans la transaction.
Je pense que je l'ai compris, est-il juste?:
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");
}
<?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";
}
?>
Comme c'est le premier résultat sur google pour "PHP mysql transaction", j'ai pensé ajouter une réponse qui démontre explicitement comment faire cela avec mysqli (comme l'auteur original 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, cela n'a pas encore été documenté par L'équipe PHP, et je suis toujours coincé dans PHP 5.3, donc je ne peux pas commenter.
Veuillez vérifier quel moteur de stockage vous utilisez. S'il s'agit de MyISAM, Transaction('COMMIT','ROLLBACK')
ne sera pas pris en charge car seul le moteur de stockage InnoDB, pas MyISAM, prend en charge les transactions.
J'ai fait une fonction pour obtenir un vecteur de requêtes et faire 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;
}
}
Lors de l'utilisation de la 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');
});
De cette façon, le code de gestion des transactions n'est pas dupliqué dans le projet. Ce qui est une bonne chose, car, à en juger par d'autres réponses PDO-ralées dans ce fil, il est facile de faire des erreurs. Les plus courants étant d'oublier de relancer l'exception et de démarrer la transaction dans le bloc try
.
J'ai eu ceci, mais je ne sais pas si c'est correct. Pourrait essayer 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 de partir d'ici: http://www.phpknowhow.com/mysql/transactions/
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);