44

据我了解,事务在我们调用$mysqli->autocommit(FALSE);语句后开始,并在调用$mysqli->commit();命令后结束,如下例所示。

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

我理解正确了吗?如果不是,请纠正我,因为这实际上是我第一次在现实生活中使用交易。

谢谢你。

4

5 回答 5

35

2020 年 11 月更新@Dharman给出了更好的答案,提供了有关 mysqli 中事务的更多详细信息,只需检查一下即可:https ://stackoverflow.com/a/63764001/569101


那么根据php doc,你是对的。

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

在上面的例子中:

  • CREATE TABLE是自动提交的,因为它是默认行为。
  • 由于. _ INSERT INTO _autocommit(FALSE)
  • DROP TABLE自动提交的,因为autocommit(FALSE)被.->commit();
于 2012-08-23T12:59:12.837 回答
33

j0k 主要是正确的,除了在 drop table 中。

没有打开自动提交->commit()

相反,DROP TABLE 是一个 DDL 查询,并且 DDL 查询始终是隐式提交的,并将提交您之前未提交的所有工作。

因此,如果您没有提交工作,DDL 查询将强制提交。

于 2013-05-24T09:02:00.757 回答
17

如何在 mysqli 中使用事务?

先决条件

为了使事务正常运行,您应该启用异常错误报告。否则mysqli不会报错,事务也不会正确执行。或者,您可以手动检查每个查询,但不建议这样做。要与 mysqli 正确连接,请使用以下 3 行:

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'pass', 'dbname');
$mysqli->set_charset('utf8mb4'); // always set the charset

事务仅适用于事务表。确保您的表存储引擎支持事务。例如,MyISAM 忽略提交/回滚。

交易

使用 mysqli 创建事务有两种可能的方法。默认情况下,所有查询/语句在执行后立即提交。您可以关闭自动提交或使用一次性事务。

在以下情况下,事务将提交到数据库:

  • 打电话时commit
  • 设置 autocommit=1 后
  • 开始另一笔交易时
  • 执行 DDL 查询时
  • 在其他一些情况下。有关更多信息,请参阅导致隐式提交的语句

使用自动提交(假)

如果您关闭自动提交,则您决定何时提交,但调用commit()不会重新打开自动提交。

//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.

使用 begin_transaction()

您可以使用begin_transaction(). 这没有设置autocommit=false,所以当您调用commit()您结束事务而不开始新事务时。

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

执行 DDL 语句

一些 SQL 语句会触发显式提交,但不会影响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);

回滚

如果发生异常,则 PHP 将结束脚本的执行,并且代码将永远不会到达commit语句。但是,在某些情况下,您可能希望显式回滚事务,例如避免在代码的其他地方意外调用 commit。

以下是此类交易的示例。第二个查询尝试插入到一个不存在的表中,这意味着 mysqli 将抛出异常。我们不是让 PHP 脚本死掉,而是捕获异常并回滚事务。该值4将永远不会插入到数据库中,因为这两个查询都已回滚。

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;
}
于 2020-09-06T12:09:34.583 回答
12

准备一次 SQL 语句,然后执行几次:

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

?>
于 2016-07-16T21:08:04.610 回答
11

你认为commit自动切换autocommittrue? PHP Doc中的一条评论说不!

于 2013-01-04T15:49:14.613 回答