transaction - Ejemplos de transacciones PHP+MySQL
mysql transaction php (9)
Realmente no he encontrado un ejemplo normal de archivo PHP donde se utilizan las transacciones de MySQL. ¿Me puede mostrar un ejemplo simple de eso?
Y una pregunta más. Ya hice mucha programación y no usé transacciones. ¿Puedo poner una función de PHP o algo en header.php
que si una mysql_query
falla, las otras fallan también?
Creo que lo he descubierto, ¿es correcto ?:
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");
}
Como este es el primer resultado en Google para "php mysql transaction", pensé que agregaría una respuesta que demuestre explícitamente cómo hacer esto con mysqli (como el autor original quería ejemplos). Aquí hay un ejemplo simplificado de transacciones con 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
}
Además, tenga en cuenta que PHP 5.5 tiene un nuevo método mysqli::begin_transaction . Sin embargo, esto no ha sido documentado aún por el equipo de PHP, y todavía estoy atascado en PHP 5.3, así que no puedo comentarlo.
Creo que lo he descubierto, ¿es correcto ?:
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");
}
Cuando se utiliza la conexión PDO:
$pdo = new PDO(''mysql:host=localhost;dbname=mydb;charset=utf8'', $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // this is important
]);
A menudo utilizo el siguiente código para la gestión de transacciones:
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
}
}
Ejemplo de uso:
transaction(function()
{
global $pdo;
$pdo->query(''first query'');
$pdo->query(''second query'');
$pdo->query(''third query'');
});
De esta manera, el código de gestión de transacciones no se duplica en todo el proyecto. Lo que es bueno, ya que, a juzgar por otras respuestas en este hilo, es fácil cometer errores. Los más comunes son olvidar volver a lanzar la excepción e iniciar la transacción dentro del bloque try
.
Hice una función para obtener un vector de consultas y hacer una transacción, tal vez alguien lo encuentre útil:
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;
}
}
La idea que generalmente utilizo cuando trabajo con transacciones se ve así: (semi-pseudo-código) :
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();
}
Tenga en cuenta que, con esta idea, si una consulta falla, se debe lanzar una excepción:
- PDO puede hacer eso, dependiendo de cómo lo configures
- Ver
PDO::setAttribute
- y
PDO::ATTR_ERRMODE
yPDO::ERRMODE_EXCEPTION
- Ver
- de lo contrario, con alguna otra API, es posible que deba probar el resultado de la función utilizada para ejecutar una consulta y lanzar una excepción usted mismo.
Desafortunadamente, no hay magia involucrada. No puede simplemente poner una instrucción en algún lugar y hacer que las transacciones se realicen de forma automática: aún debe especificar qué grupo de consultas se deben ejecutar en una transacción.
Por ejemplo, con bastante frecuencia tendrá un par de consultas antes de la transacción (antes del begin
) y otro par de consultas después de la transacción (después de commit
o rollback
) y deseará que esas consultas se ejecuten sin importar lo que haya sucedido (o No) en la transacción.
Por favor, compruebe qué motor de almacenamiento está utilizando. Si es MyISAM, entonces no se admitirá Transaction(''COMMIT'',''ROLLBACK'')
porque solo el motor de almacenamiento InnoDB, no MyISAM, admite transacciones.
Tuve esto, pero no estoy seguro si esto es correcto. Podría probar esto también.
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");
}
Idea desde aquí: http://www.phpknowhow.com/mysql/transactions/
Un ejemplo más de estilo de procedimiento con mysqli_multi_query
, asume que $query
está lleno de declaraciones separadas por punto y coma.
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);
<?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";
}
?>