PHP + MySQL事务示例

goo*_*ing 284 php mysql transactions

我真的没有找到正在使用MySQL事务的PHP文件的正常示例.你能告诉我一个简单的例子吗?

还有一个问题.我已经做了很多编程,但没有使用交易.header.php如果一个mysql_query失败了,那么我可以放一个PHP函数或其他东西吗?


我想我已经弄明白了,是不是?:

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");
}
Run Code Online (Sandbox Code Playgroud)

Pas*_*TIN 319

我在处理事务时通常使用的想法如下(半伪代码):

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();
}
Run Code Online (Sandbox Code Playgroud)


请注意,有了这个想法,如果查询失败,则必须抛出异常:

  • PDO可以这样做,具体取决于您的配置方式
  • 否则,使用其他API,您可能必须测试用于执行查询的函数的结果,并自己抛出异常.


不幸的是,没有任何魔法涉及.您不能只是在某处放置指令并自动完成事务:您仍然必须具体说明必须在事务中执行哪组查询.

例如,在交易之前(在此之前begin)和交易之后的另一对查询(在任一commit或之后rollback),您经常会有一些查询,并且无论发生了什么(或不发生),您都希望执行这些查询.交易.

  • 如果您正在执行可能抛出除db之外的异常的操作,请小心.如果是这样,非db语句的异常可能会导致无意中回滚(即使所有db调用都成功).通常情况下,即使错误不在数据库端,您也会认为回滚是一个好主意,但有时候第三方/非关键代码可能会导致不那么重要的异常,并且您仍然希望继续交易. (32认同)
  • 什么是`$ db`类型?库MySQLi? (6认同)
  • @Jake有关使用mysqli的示例,请参阅我的答案(类似于Pascal的方法). (3认同)
  • 它可以很容易地修改以捕获`PDOException`,甚至可以根据需要检查异常值.http://us2.php.net/PDOException (2认同)

goo*_*ing 107

我想我已经弄明白了,是不是?:

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");
}
Run Code Online (Sandbox Code Playgroud)

  • 无需设置autocommit = 0.交易总是以这种方式运作. (26认同)
  • 我想一旦你开始一个事务,它就像AUTOCOMMIT = 0一样工作 (6认同)
  • @babonk是对的.一旦事务启动,就会隐式设置AUTOCOMMIT = 0,并且在事务通过提交或回滚结束后,MySql会设置在启动事务之前使用的AUTOCOMMIT值.注意:您不应该设置AUTOCOMMIT = 0,因为如果您决定插入/更新另一行后提交更改,则应明确提交. (4认同)
  • 引擎商店应该是InnoDB,而不是MyISAM! (4认同)
  • @babonk-不知道InnoDB是这种情况吗? (2认同)

Ged*_*lex 39

<?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";
}

?>
Run Code Online (Sandbox Code Playgroud)

  • 他的评论使这个例子非常清楚.好的代码不需要描述文本.此问题还要求一个简单的例子.我喜欢这个答案. (19认同)
  • 对不起im begginer,我的英文不好,很容易检查代码 - 对于begginers - commit()rollback()begin()放在类DB(例如),$ query - 不是一次 - 也许是$ query0 $ query1 - 然后谢谢他们 - 我使用这段代码,这很容易理解=) (6认同)
  • 欢迎使用StackOverflow.请始终在答案中写一些描述文字. (3认同)

Ele*_*One 35

由于这是google上"php mysql transaction"的第一个结果,我想我会添加一个明确演示如何使用mysqli执行此操作的答案(正如原作者想要的例子).这是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   
}
Run Code Online (Sandbox Code Playgroud)

另外,请记住PHP 5.5有一个新方法mysqli :: begin_transaction.但是,PHP团队还没有记录这一点,我仍然坚持使用PHP 5.3,所以我无法评论它.

  • 在相关的说明中,我刚刚发现,如果您正在使用InnoDB表,则可以在使用autocomitt()方法处理事务时锁定/解锁表,但使用begin_transaction()方法时不可能:[ MySQL文档](http://dev.mysql.com/doc/refman/5.0/en/lock-tables-and-transactions.html) (2认同)
  • @Hamid` $ conn-> autocommit(FALSE)`,在上面的示例中,仅影响单个连接 - 它对数据库的任何其他连接没有影响. (2认同)
  • 注意:如果查询能够返回评估为 false 或 0 的有效结果,则应该执行 `if (result === false)` 而不是 `if (!result)`。 (2认同)

小智 8

请检查您使用的存储引擎.如果它是MyISAM,Transaction('COMMIT','ROLLBACK')则不支持,因为只有InnoDB存储引擎而不是MyISAM支持事务.


小智 5

我创建了一个函数来获取查询向量并执行事务,也许有人会发现它很有用:

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)


Dan*_*tov 5

使用PDO连接时:

$pdo = new PDO('mysql:host=localhost;dbname=mydb;charset=utf8', $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // this is important
]);
Run Code Online (Sandbox Code Playgroud)

我经常使用以下代码进行事务管理:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

transaction(function()
{
    global $pdo;

    $pdo->query('first query');
    $pdo->query('second query');
    $pdo->query('third query');
});
Run Code Online (Sandbox Code Playgroud)

这样,事务管理代码不会在整个项目中重复.这是一件好事,因为从这个帖子中的其他PDO相关答案判断,它很容易出错.最常见的是忘记重新抛出异常并在try块内启动事务.