【问题标题】:PHP/PDO::commit doesn't work in "try" blockPHP/PDO::commit 在“try”块中不起作用
【发布时间】:2012-03-24 05:40:04
【问题描述】:

当我将“PDO::commit()”方法放入“try”块时,它不起作用,即使 SQL 代码正常,我也没有得到“commit”。

$conn->dbh->beginTransaction();

$stmt = $conn->dbh->prepare('some SQL-code');

$stmt->bindValue(...);

try 
{ 
    $stmt->execute();
    $conn->dbh->commit();
} 
catch (Exception $e) 
{
    $dbh->rollBack();
    echo $e->getMessage();
}

但如果我把它放在“catch”块之后,一切正常。

...
try 
{ 
    $stmt->execute();
} 
catch (Exception $e) 
{
    $dbh->rollBack();
    echo $e->getMessage();
}
$conn->dbh->commit();

这是预期的行为吗?为什么 PDO::commit() 在“try”块中不起作用?

只是想为自己澄清一下,因为我已经花了整整两个小时来找到解决这个问题的方法,但我不确定这个解决方案是否正确。


代码如下:

Class Connection
{
public $dbh;
private static $instance;

private function __construct()
{
    $config = parse_ini_file('config.ini');
    $dsn = $config['db.dbms'] . ':host=' . $config['db.host'] .
           ';dbname='    . $config['db.dbname'] .
           ';port='      . $config['db.port'] .
           ';connect_timeout=15';
    $this->dbh = new PDO($dsn, $config['db.user'], $config['db.password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
    $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

public static function getInstance()
{
    if (!isset(self::$instance))
    {
        $object = __CLASS__;
        self::$instance = new $object;
    }
    return self::$instance;
}
}

Class NestedSet
{
public function insertAsLastChildOf ($parentnode)
{
    $fields = '';
    $params = '';

    $conn = Connection::getInstance();

    $conn->dbh->beginTransaction();

    foreach ($this->_modelfields as $field => $type)
    {
        $fields .= '`'.$field.'`, ';
        $params .= ':'.$field.', ';            
    }

    $stmt = $conn->dbh->prepare('UPDATE `' . $this->_tablename . '`
                           SET `rgt` = `rgt` + 2
                           WHERE `rgt` >= :parentnodergt;


                           UPDATE `' . $this->_tablename . '`
                           SET `lft` = `lft` + 2
                           WHERE `lft` > :parentnodergt;


                           INSERT INTO `' . $this->_tablename . '`  
                              (' . $fields . '
                               `lft`,
                               `rgt`,
                               `level`)
                            SELECT  
                                ' . $params . '
                                `rgt` - 2, 
                                `rgt` - 1, 
                                `level` + 1                                    
                            FROM `' . $this->_tablename . '`
                            WHERE `id` = :parentnodeid;');

    foreach ($this->_modelfields as $field => $type)
    {
        $pdoparam = (stripos($type, 'int') === 0) ? PDO::PARAM_INT : PDO::PARAM_STR;
        $stmt->bindValue(':'.$field.'', $this->$field, $pdoparam);
    }

    $stmt->bindValue(':parentnodergt', $parentnode->rgt, PDO::PARAM_INT);
    $stmt->bindValue(':parentnodeid', $parentnode->id, PDO::PARAM_INT);

    try
    {
        $stmt->execute();
        $conn->dbh->commit();
    }
    catch (PDOException $e)
    {
        $conn->dbh->rollBack();
        echo $e->getMessage() . '<br/> file - ' . __FILE__ . '<br/> line - ' . __LINE__ . '<br/>';
    }

    unset($stmt); 
}
}

我尝试重写代码,以便每个准备好的语句都有一个查询。在这种情况下,一切正常 - 如果没有错误,我会执行代码并获得异常,否则会回滚。

这是代码

    $stmt_1 = $conn->dbh->prepare('UPDATE `' . $this->_tablename . '`
                            SET `rgt` = `rgt` + 2
                            WHERE `rgt` >= :parentnodergt;');
    $stmt_1->bindValue(':parentnodergt', $parentnode->rgt, PDO::PARAM_INT);

    $stmt_2 = $conn->dbh->prepare('UPDATE `' . $this->_tablename . '`
                            SET `lft` = `lft` + 2
                            WHERE `lft` > :parentnodergt;');
    $stmt_2->bindValue(':parentnodergt', $parentnode->rgt, PDO::PARAM_INT);

    $stmt_3 = $conn->dbh->prepare('INSERT INTO `' . $this->_tablename . '`  
                              (' . $fields . '
                               `lft`,
                               `rgt`,
                               `level`)
                            SELECT  
                                ' . $params . '
                                `rgt` - 2, 
                                `rgt` - 1, 
                                `level` + 1                                    
                            FROM `' . $this->_tablename . '`
                            WHERE `id` = :parentnodeid;');

    foreach ($this->_modelfields as $field => $type)
    {
        $pdoparam = (stripos($type, 'int') === 0) ? PDO::PARAM_INT : PDO::PARAM_STR;
        $stmt_3->bindValue(':'.$field.'', $this->$field, $pdoparam);
    }

    $stmt_3->bindValue(':parentnodeid', $parentnode->id, PDO::PARAM_INT);

    try
    {
        $stmt_1->execute();
        $stmt_2->execute();
        $stmt_3->execute();
        $conn->dbh->commit();
    }
    catch (PDOException $e)
    {
        $conn->dbh->rollBack();
        echo $e->getMessage() . '<br/> file - ' . __FILE__ . '<br/> line - ' . __LINE__ . '<br/>';
    }
    unset($stmt);

这是否意味着 PDO::prepare() 不应该与多个查询一起使用?

我之所以问,是因为我在 PDO 手册中没有找到任何相关内容。

【问题讨论】:

  • 您是否为您的 PDO 实例启用了异常抛出? $dbh-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  • 是的,我已经把它放在连接类(单例)的 __construct() 中:$this-&gt;dbh = new PDO(...);$this-&gt;dbh-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 如果我在 SQL 代码中有错误,我会抛出一个异常。
  • 尝试引发异常,看看是否真的有效。
  • 另外,如果事务失败,您希望抛出哪个特定异常?在您的代码中,它是Exception,这并不具体。那么你能补充一些参考吗?
  • 这很奇怪,但即使我在 SQL 代码中有错误,我也没有收到异常。所以我需要修改我的代码。

标签: php exception transactions pdo


【解决方案1】:

您的查询有问题。它在 t/c 块中不起作用,因为execute() 导致错误,因此在commit() 调用之前引发了异常。当你在 t/c 块之后移动它时它起作用的唯一原因是因为它是一个程序步骤。尝试跟踪您的错误日志。

【讨论】:

  • 你能在初始分配后做一个var_dump($stmt); 吗?那些多个查询语句很乱,我很好奇它是否有效。
  • 我试过一次做一个查询,它可以工作。在这种情况下,如果查询中有错误,我会抛出异常。例如,如果我只留下 INSERT 查询并尝试为 PK 字段创建重复条目,我会收到以下异常 SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '5th' for key 'reference' file - C:\...\classes\NestedSet.php line - 187
  • 当您将它们作为单独的语句时,您是否将它们全部放在同一个事务中?
  • 是的,一个事务中的多个准备好的语句(每个准备好的语句一个查询)可以工作,而一个包含多个查询的准备好的语句不起作用。
  • 这取决于您所说的“解决”是什么意思。一方面是的。我刚刚将带有多查询的准备好的语句拆分为一个事务中的多个语句(每个准备好的语句一个查询)。另一方面没有。我无法在事务中使用多查询制作单个准备好的语句。显然这是一个 PDO 错误。这是关于这个问题的discussion
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
  • 1970-01-01
  • 2023-01-16
  • 2013-01-03
  • 2011-02-11
相关资源
最近更新 更多