¿Cómo iniciar y finalizar una transacción en mysqli?
Hasta donde tengo entendido, la transacción comienza una vez que llamamos a $mysqli->autocommit(FALSE);
la declaración y finaliza después de llamar $mysqli->commit();
al comando como en el siguiente ejemplo.
<?php
//Start transaction
$mysqli->autocommit(FALSE);
$mysqli->query('UPDATE `table` SET `col`=2');
$mysqli->query('UPDATE `table1` SET `col1`=3;');
$mysqli->commit();
//End transaction
//Executing other queries without transaction control
$mysqli->query("Select * from table1");
$mysqli->query("Update table1 set col1=2");
//End of executing other queries without transaction control
//Start transaction
$mysqli->autocommit(FALSE);
$mysqli->query('UPDATE `table` SET `col`=2');
$mysqli->query('UPDATE `table1` SET `col1`=3;');
$mysqli->commit();
//End transaction
?>
¿He entendido correctamente? Si no, ¿podría corregirme, porque en realidad es la primera vez que utilizo transacciones en la vida real?
Gracias.
Actualización de noviembre de 2020 : @Dharman dio una mejor respuesta con más detalles sobre las transacciones en mysqli, simplemente verifíquelo: https://stackoverflow.com/a/63764001/569101 👇
Bueno, según el documento php , tienes razón.
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$mysqli->query("CREATE TABLE Language LIKE CountryLanguage");
/* set autocommit to off */
$mysqli->autocommit(FALSE);
/* Insert some values */
$mysqli->query("INSERT INTO Language VALUES ('DEU', 'Bavarian', 'F', 11.2)");
$mysqli->query("INSERT INTO Language VALUES ('DEU', 'Swabian', 'F', 9.4)");
/* commit transaction */
$mysqli->commit();
/* drop table */
$mysqli->query("DROP TABLE Language");
/* close connection */
$mysqli->close();
?>
En el ejemplo anterior:
- el
CREATE TABLE
confirma automáticamente porque es el comportamiento predeterminado. INSERT INTO
no están comprometidos automáticamente debido aautocommit(FALSE)
.- se
DROP TABLE
confirma automáticamente porqueautocommit(FALSE)
fue reiniciado por->commit();
.
j0k tiene razón en gran medida, excepto en la tabla desplegable.
La confirmación automática no está activada con el->commit()
En cambio, DROP TABLE es una consulta DDL, y las consultas DDL siempre se confirman implícitamente y confirmarán todo el trabajo que no se haya comprometido previamente.
Entonces, si no confirmó el trabajo, la consulta DDL forzaría esta confirmación.
¿Cómo utilizar transacciones en mysqli?
Requisito previo
Para que las transacciones se comporten correctamente, debe habilitar el informe de errores de excepción. De lo contrario, mysqli no informará errores y la transacción no se realizará correctamente. Alternativamente, puede verificar manualmente cada consulta, pero no se recomienda. Para conectarse correctamente con mysqli utilice las siguientes 3 líneas:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'pass', 'dbname');
$mysqli->set_charset('utf8mb4'); // always set the charset
Las transacciones solo funcionan con tablas transaccionales. Asegúrese de que su motor de almacenamiento de tablas admita transacciones. Por ejemplo, MyISAM ignora la confirmación/reversión.
Actas
Hay dos formas posibles de crear una transacción usando mysqli. De forma predeterminada, todas las consultas/declaraciones se confirman tan pronto como se realizan. Puede desactivar la confirmación automática o utilizar una transacción única.
Las transacciones se confirman en la base de datos en las siguientes situaciones:
- al llamar
commit
- después de configurar autocommit = 1
- al iniciar otra transacción
- al realizar una consulta DDL
- y en algunas otras situaciones. Para obtener más información, consulte Declaraciones que provocan una confirmación implícita.
Usando confirmación automática (falso)
Si desactiva la confirmación automática, usted decide cuándo desea realizar la confirmación, pero llamar commit()
no vuelve a activar la confirmación automática.
//Start transaction
$mysqli->autocommit(false);
$mysqli->query('INSERT INTO director(name) VALUE("Steven Spielberg")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Jurassic Park';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
$mysqli->commit();
// Changes are committed, but autocommit is not switched back on
// Following queries are still transactional.
// They will not be committed unless you call commit or switch autocommit back on
$mysqli->query('INSERT INTO director(name) VALUE("James Cameron")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Titanic';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
$mysqli->autocommit(true);
// All queries are committed and everything that follows will be immediately committed.
Usando comenzar_transacción()
Puede iniciar una transacción única usando begin_transaction()
. Esto no significa autocommit=false
que cuando llame commit()
finalice la transacción sin iniciar una nueva.
//Start transaction
$mysqli->begin_transaction();
$mysqli->query('INSERT INTO director(name) VALUE("Steven Spielberg")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Jurassic Park';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
$mysqli->commit();
// Changes are committed and the transaction has ended
// Following queries will be committed one by one as soon as they are peformed.
$mysqli->query('INSERT INTO director(name) VALUE("James Cameron")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Titanic';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
Realizar declaraciones DDL
Algunas sentencias SQL desencadenan una confirmación explícita pero no afectan el valor de autocommit
.
//Start transaction
$mysqli->autocommit(false);
$mysqli->query('INSERT INTO director(name) VALUE("Steven Spielberg")');
$directorId = $mysqli->insert_id;
$movieTitle = 'Jurassic Park';
$stmt = $mysqli->prepare('INSERT INTO movie(title, directorId) VALUE(?,?)');
$stmt->bind_param('ss', $movieTitle, $directorId);
$stmt->execute();
// The following will call commit but it will not set autocommit=true
$mysqli->query('TRUNCATE TABLE movie_genre');
// if you want to switch autocommit back on, you have to call:
$mysqli->autocommit(true);
Retroceder
Si ocurre una excepción, PHP finalizará la ejecución del script y el código nunca llegará a la commit
declaración. Sin embargo, en algunas situaciones, es posible que desee revertir la transacción explícitamente, por ejemplo, para evitar llamar accidentalmente a commit en algún otro lugar del código.
A continuación se muestra un ejemplo de cómo sería una transacción de este tipo. La segunda consulta intenta insertarse en una tabla inexistente, lo que significa que mysqli generará una excepción. En lugar de dejar que el script PHP muera, detectamos la excepción y revertimos la transacción. El valor 4
nunca se insertará en la base de datos porque ambas consultas se revirtieron.
try {
// Start transaction
$mysqli->begin_transaction();
$mysqli->query('INSERT INTO some_table(col2) VALUE(4)');
$mysqli->query('INSERT INTO does_not_exist(col2) VALUE(4)');
// Commit changes
$mysqli->commit();
} catch (\Throwable $e) {
// Something went wrong. Rollback
$mysqli->rollback();
// Rethrow the exception so that PHP does not continue
// with the execution and the error can be logged in the error_log
throw $e;
}
Prepare la declaración SQL UNA VEZ y luego ejecútela VARIAS veces:
<?php
$Mysqli = new mysqli("host","user","pass","base");
// check connection
if(mysqli_connect_errno())
{
printf("Connect failed: %s\n",mysqli_connect_error());
exit();
}
// some data for db insertion
$countries=['Austria','Belgia','Croatia','Denmark','Estonia'];
// explicitly begin DB transaction
$Mysqli->begin_transaction();
// prepare statement (for multiple inserts) only once
$stmt=$Mysqli->prepare("INSERT INTO table(column) VALUES(?)");
// bind (by reference) prepared statement with variable $country
$stmt->bind_param('s',$country);
// load value from array into referenced variable $country
foreach($countries as $country)
{
//execute prep stat more times with new values
//$country is binded (referenced) by statement
//each execute will get new $country value
if(!$stmt->execute())
{
// rollback if prep stat execution fails
$Mysqli->rollback();
// exit or throw an exception
exit();
}
}
// close prepared statement
$stmt->close();
// commit transaction
$Mysqli->commit();
// close connection
$Mysqli->close();
?>