Php 如何在mysqli中启动和结束事务?

Php 如何在mysqli中启动和结束事务?,php,transactions,mysqli,Php,Transactions,Mysqli,据我所知,一旦我们调用$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;'); $mysq

据我所知,一旦我们调用
$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
?>

我理解对了吗?如果没有,请纠正我,因为这实际上是我第一次在现实生活中使用事务


谢谢。

更新Novenbre 2020:提供了一个更好的答案,提供了有关mysqli中事务的更多详细信息,只需检查它:您认为
提交
会自动将
自动提交
切换回
true

报纸上的评论说不

j0k基本正确,除了在下降表中

使用
->commit()

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


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

准备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();

?>

如何在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
交易 使用mysqli创建事务有两种可能的方法。默认情况下,所有查询/语句一经执行即提交。您可以关闭自动提交或使用一次性事务

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

  • 调用
    commit
  • 设置自动提交后=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_事务() 您可以使用
begin\u 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;
}

Per@Patec如下:提交不会重新打开自动提交;请参阅“我知道您无法删除此答案”,但我希望您重新查看并改进此答案。您复制的PHP手册页面包含一个不完整的示例,该示例没有显示如何正确使用事务。现在已删除此页面,并提供了一个新示例。因为你的答案是公认的,它描绘了一幅图画,这是正确的解决方案,但事实并非如此。求求你,如果你能改进它吗?哇,很老的答案。我知道你的答案更好,我没有时间改进我自己的答案。所以我已经编辑了我的答案,以链接到你的答案。我不认为会。直观地说,我假设“commit()”执行查询,而“autocommit()”将mysqli对象的autocommit属性切换为true或false。可以使用$msqli->begin_transaction()、$mysqli->rollback()和$mysqli->commit();而不是显式地关闭自动提交功能,然后再打开。对于那些想知道:DDL意味着数据定义语言。有关DDL、DQL、DML、DCL和TCL之间的区别,请参阅。@Minding我可能几年前就应该提供pearl了。我还应该声明
$mysqli->commit()不会神奇地打开自动提交。六年过去了,我们知道我们应该回答更多的问题:)你在这里的例子是
begin\u transaction()
可以改进;如果有人在表中插入相同的数据两次,他们很可能希望该数据出现一次,而不是使用事务使其出现零次。MySQL事务的要点是,如果链接更新失败,它们会回滚有效更新,简单地复制SQL插入并不能反映这一点。@Martin说实话,我不认为我完全理解你的评论,但如果可能的话,我想改进答案。我对上一个示例做了一个小改动,以更清楚地显示哪个查询失败。如果您能多解释一下您的建议,我将不胜感激;例如,通过填充订单篮表和订单主表来构建订单。因此,在
movie
表中插入一个有效的电影可能更合适,另一列引用电影制作人表,其中制作人名称/dob等无效,因此如果电影制作人无效,则不添加电影。仅仅是这个主题的一个想法(事务有用的实际案例)似乎很难找到实际的例子。