【问题标题】:PDO Transaction doesn catch exceptionsPDO 事务不捕获异常
【发布时间】:2011-10-27 16:34:30
【问题描述】:

我是第一次尝试 PDO 事务。下面的代码不起作用。我们尝试插入的电子邮件地址有重复,因此它应该会失败。它确实给了我一个错误。但是第一个插入被插入到数据库中并且它不会回滚。如果我在提交之前将 PDO::rollBack 移动到 Try{ 中,我知道回滚工作原因,它确实会回滚。我认为问题在于它没有捕捉到错误,因此没有调用 PDO::rollBack。有什么想法吗?

try {
    PDO::beginTransaction();

$sql = "INSERT INTO .`tblUsersIDvsAgencyID` (`id`, `agency_id`) VALUES (NULL, :agencyID)";
$STH = $this->prepare($sql); 
$STH->bindParam(':agencyID', $AgencyUser['agency_id']);
$STH->execute();
$userID = parent::lastInsertId();

$sql = "INSERT INTO `tblUsersEmailAddress` (`id`, `user_id`, `email_address`, `primary`, `created_ts`, `email_verified`) VALUES (NULL ,  :userID ,  :EmailAddress ,  '1', CURRENT_TIMESTAMP ,  '0' )";
$STH = $this->prepare($sql); 
$STH->bindParam(':userID', $userID);
$STH->bindParam(':EmailAddress', $email_address);
$STH->execute();
PDO::commit();
echo 'Data entered successfully<br />';
}
catch(PDOException $e)
{
/*** roll back the transaction if we fail ***/
PDO::rollBack();
echo "failed";
} 

【问题讨论】:

  • 你为什么要像PDO::beginTransaction();这样静态调用事务方法?您应该非静态地调用它们,例如:$this-&gt;beginTransaction();。至少,我认为它需要一个特定的连接才能知道在哪里应用事务调用。

标签: php mysql pdo


【解决方案1】:

PDO::beginTransaction() 不是静态方法。从您的问题来看,您似乎正在扩展 PDO 类。我不会那样做,因为我怀疑你是否在基类中添加了任何重要的东西。相反,您应该将 PDO 连接设置为类属性。

例如

class ParentClass
{
    /**
     * @var PDO
     */
    protected $dbh;

    public function __construct(PDO $dbh)
    {
        $this->dbh = $dbh;

        // Make sure PDO is set to throw exceptions
        $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
}

class ChildClass extends ParentClass
{
    public function insertStuff()
    {
        $this->dbh->beginTransaction();
        try {
            // do stuff

            $this->dbh->commit();
        } catch (PDOException $e) {
            $this->dbh->rollBack();
            throw $e;
        }
    }
}

【讨论】:

  • @Phil ya iam 将这个类扩展到另一个扩展到 PDO 的父类。我这样做的原因是当我从这个类调用一个函数到它的父类时,并且父类执行一些与数据库相关的函数,它将使用相同的连接。
  • @fireeyedboy 我该怎么做?我应该让这个类的构造函数建立 DBH 连接吗?
  • @MofCA 我认为您不了解继承的概念。当您调用 parent:: 方法时,您不会调用另一个对象的操作。这是同一个例子。但是,我的评论仍然有效,您最好扩展 PDO
  • @M.:最好将 PDO 对象传递给消费对象的构造函数,如下所示:pastie.org/2378109 这样,您可以轻松地创建消费对象的另一个实例另一个 PDO 连接。这称为松散耦合 (en.wikipedia.org/wiki/Loose_coupling)。它使您的代码保持灵活,而不是通过硬编码(例如在构造函数中)将其捆绑在一起。
  • @MofCA 我已经用更全面的 OOP 实施更新了我的答案
【解决方案2】:

我只是从引用文档开始:

注意:某些 MySQL 表类型(存储引擎)不支持事务。当使用不支持事务的表类型编写事务数据库代码时,MySQL 会假装事务已成功启动。此外,发出的任何 DDL 查询都会隐式提交任何待处理的事务。

您的问题可能是预期行为。另外:

  1. beginTransaction 不是静态的。 (我重复 Phil 的声明,即您不应该扩展 PDO)。
  2. 调用$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  3. 您永远不会在您的声明中调用closeCursor(这通常会导致问题)。 (Phil 指出,在这种情况下没有明确的必要性。但它仍然是最佳实践)。
  4. 使用bindParam 不会对用户ID 变量产生任何好处,因为您正在使用它(一个未重复使用的本地定义变量)。

【讨论】:

  • 3.您不需要在插入语句上调用closeCursor()。该语句在执行后立即关闭。 4、Preparing statements编译并经常将语句缓存在DB服务器上。如果使用不同的参数再次运行此查询,好处是显而易见的。基本上,所有变量(非常量)查询“值”都应该参数化
  • @Phil 3 -- 膝跳反应。我已经看到开放游标造成严重破坏。这意味着现在,每当我看到没有closeCursor 的第二个查询时,我都会指出它。 4. 不幸的是,这是我不准确的一个案例——我可以说得更好,我已经改写了一点。
  • 哦,我听说你要关闭游标了。我总是关闭我的SELECT 声明,即使他们已经完成了他们的课程。
猜你喜欢
  • 2014-06-19
  • 2012-07-09
  • 1970-01-01
  • 2012-11-03
  • 2012-05-20
  • 2015-10-18
  • 2015-02-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多