PHP PDO - 没有活动事务

Posted

技术标签:

【中文标题】PHP PDO - 没有活动事务【英文标题】:PHP PDO - There is no active transaction 【发布时间】:2017-05-14 13:30:16 【问题描述】:

我遇到了 php 脚本中的事务问题。如果至少其中一个失败,我想进行多次查询并能够全部召回它们。您可以在下面找到我正在使用的脚本的一个简单示例:

$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',  
DB_USER, DB_PASSW, array(  
    PDO::ATTR_EMULATE_PREPARES => false,  
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));

    $conn->beginTransaction();

    $sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
    $stmt = $conn->prepare($sql);  
    if(count($tags_input)>0)
            for($i = 0;$i<count($tags_input);$i++)
                    $stmt->bindValue(':val0', 57); 
                    $stmt->bindValue(':val1', $tags_input[$i]); 
                    $stmt->bindValue(':val2', 'Y'); 
                    $result = $stmt->execute();

            

    

    $res1 = $conn->commit();
    $conn->rollBack();

现在,这个例子产生了一个错误:

未捕获的异常“PDOException”和消息“没有活动 交易”

如果我删除$conn-&gt;rollBack(); 行,错误就会消失。因此我无法理解,为什么pdo 对象看不到打开的事务(begintransactioncommit 不会产生任何错误)。我还尝试将rollBack() 放入事务中,但没有任何区别。我仍然收到错误消息“没有活动交易”。

我在 InnoDB 上运行 PHP 5.6 和 Mysql 表。

【问题讨论】:

你在这里提交事务:`$res1 = $conn->commit();` 所以你不能回滚 尝试将您的事务代码包装到 try-catch 语句中,如php.net/manual/en/pdo.transactions.php 中的第一个示例所示 phpdelusions.net/pdo#transactions @e4c5 感谢您的意见。这是否意味着一旦您提交事务就不能回滚?在那种情况下,我不确定我是否在回滚中看到了感觉,就好像我遇到了一个错误,我只是不提交事务.. 【参考方案1】:

将您的事务代码包装在 try-catch 语句中。

//try 
$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',  
DB_USER, DB_PASSW, array(  
    PDO::ATTR_EMULATE_PREPARES => false,  
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
 catch (Exception $e) 
  die("Unable to connect: " . $e->getMessage());
    
try   
    $conn->beginTransaction();   
    $sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
    $stmt = $conn->prepare($sql);  
    if(count($tags_input)>0)
            for($i = 0;$i<count($tags_input);$i++)
                    $stmt->bindValue(':val0', 57); 
                    $stmt->bindValue(':val1', $tags_input[$i]); 
                    $stmt->bindValue(':val2', 'Y'); 
                    $result = $stmt->execute();
            
    
$res1 = $conn->commit();    
 catch (Exception $e) 
  $conn->rollBack();
  echo "Failed: " . $e->getMessage();

编辑

Richard 作为评论提供了一个非常有根据且直接的答案解释。

您收到错误的原因是您试图关闭已经关闭的事务。 beginTransaction 打开一个,然后 rollBackcommit 关闭它。您必须避免对单个 beginTransaction 语句执行 BOTH 操作,即提交/回滚,否则您会收到错误消息。上面的 try/catch 代码确保只执行一个结束语句。

【讨论】:

您好,感谢您的快速回答。它似乎工作!但是,我不确定为什么。没有 try/catch 块就不可能使用转换吗?另外,我认为这里的回滚是无关紧要的,因为我试图将它从代码中取出并且也没有在数据库中获得任何新记录(这是这个想法,但不确定为什么会发生) 这与@e4c5 在评论中所写的原因相同。我很高兴它解决了你的问题,圣诞快乐 :) 只是补充一点,你得到错误的原因是因为你试图关闭一个已经关闭的事务。 “beginTransaction”打开一个,“rollBack”或“commit”关闭它。您必须避免对单个“beginTransaction”语句同时执行提交/回滚,否则您将收到错误消息。上面的 try/catch 代码确保只执行 1 个“关闭”语句。 @RichardDuerr 谢谢,非常好的和快速的解释,我在上面的编辑中添加了它。【参考方案2】:

Peter 和 Richards 的答案已经是正确的,但是交易结构中的代码有一个小错误(我无法添加评论)。

$connection-&gt;beginTransaction() 必须在 try-catch 块之外。当您在try-block 中启动beginTransaction() 并且您的数据库操作引发异常时,catch-block 不知道来自活动事务的某些内容。所以,你会得到同样的错误:

“没有活动的交易”。

所以结构也应该是这样的:

    获取连接。 使用$connection-&gt;beginTransaction() 启动事务 打开try-catch 块。

try-block 在 DB 操作之后包含 $connection-&gt;commit()

catch-block 在抛出异常之前包含$connection-&gt;rollback()

所以你的代码应该是这样的:

$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',  
DB_USER, DB_PASSW, array(  
    PDO::ATTR_EMULATE_PREPARES => false,  
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
 catch (Exception $e) 
  die("Unable to connect: " . $e->getMessage());
    
//Begin Transaction
$conn->beginTransaction();   
try   
    $sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
    $stmt = $conn->prepare($sql);  
    if(count($tags_input)>0)
            for($i = 0;$i<count($tags_input);$i++)
                    $stmt->bindValue(':val0', 57); 
                    $stmt->bindValue(':val1', $tags_input[$i]); 
                    $stmt->bindValue(':val2', 'Y'); 
                    $result = $stmt->execute();
            
    
$res1 = $conn->commit();    
 catch (Exception $e) 
  $conn->rollBack();
  echo "Failed: " . $e->getMessage();

【讨论】:

以上是关于PHP PDO - 没有活动事务的主要内容,如果未能解决你的问题,请参考以下文章

PHP PDO 事务与自动提交

PDO 事务是不是涵盖 PDO::query()?

PDO 事务不工作

有没有办法强制 PHP 等待 MySQL 完成事务?

一个事务中有多个 CREATE TABLE 错误 - 没有 PDO 异常

PHP PDO 在事务中创建多个表