【问题标题】:PHP PDO MySQL Transaction code structurePHP PDO MySQL 事务代码结构
【发布时间】:2011-12-23 16:53:24
【问题描述】:

我正在尝试使用 PHP/PDO 在 MySQL 中设置我的第一个事务...

我有一个小问题,确定上一个查询是否成功的最佳方法是什么?这是我现在所拥有的,但我宁愿找到一种方法来使用 if 语句测试查询。

这几乎是模拟代码,试图获得一个工作模型。我知道 $results 没有有效地测试任何东西是好是坏。我把它作为真正交易的占位符时间到了。。

if ($_POST['groupID'] && is_numeric($_POST['groupID'])) {
    $sql = "SET AUTOCOMMIT=0";
    $dbs = $dbo->prepare($sql);
    $dbs->execute();

    $sql = "START TRANSACTION";
    $dbs = $dbo->prepare($sql);
    $dbs->execute();

    $sql = "DELETE FROM users_priveleges WHERE GroupID=:groupID";
    $dbs = $dbo->prepare($sql);
    $dbs->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
    $dbs->execute();

    try {
        $sql = "DELETE FROM groups WHERE GroupID=:groupID LIMIT 1";
        $dbs = $dbo->prepare($sql);
        $dbs->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
        $dbs->execute();

        $results["error"] = null;
        $results["success"] = true;

        try {
            $sql = "DELETE FROM users WHERE Group=:groupID";
            $dbs = $dbo->prepare($sql);
            $dbs->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
            $dbs->execute();

            $results["error"] = null;
            $results["success"] = true;

            $sql = "COMMIT";
            $dbs = $dbo->prepare($sql);
            $dbs->execute();
        }
        catch (PDOException $e) {
            $sql = "ROLLBACK";
            $dbs = $dbo->prepare($sql);
            $dbs->execute();

            $results["error"] = "Could not delete associated users! $e";
            $results["success"] = false;
        }   
    }
    catch (PDOException $e)
    {
        $sql = "ROLLBACK";
        $dbs = $dbo->prepare($sql);
        $dbs->execute();

        $results["error"] = "COULD NOT REMOVE GROUP! $e";
        $results["success"] = false;
    }
}

【问题讨论】:

  • 为什么不使用 PDO 的 beginTransaction()、commit() 和 rollback() 方法?
  • 大声笑我今天早上才知道 beginTransaction 方法......我想其他两个在那里,但还没有查到它们。那在 TODO 清单上,谢谢!
  • 另外,您不必准备()每个语句,实际上对于您不打算插入任何变量的语句来说这是相当浪费的。只需使用 query() 运行那些。节省两行代码和不必要的准备。

标签: php mysql pdo


【解决方案1】:

一些一般说明: 不要使用bindParam(),除非您使用修改参数值的过程 因此,use bindValue()。 bindParam() 接受参数值作为引用变量。这意味着你不能做$stmt->bindParam(':num', 1, PDO::PARAM_INT); - 它会引发错误。 此外,PDO 有自己的事务控制功能,您无需手动执行查询。

我稍微重写了您的代码,以阐明如何使用 PDO:

if($_POST['groupID'] && is_numeric($_POST['groupID']))
{
    // List the SQL strings that you want to use
    $sql['privileges']  = "DELETE FROM users_priveleges WHERE GroupID=:groupID";
    $sql['groups']      = "DELETE FROM groups WHERE GroupID=:groupID"; // You don't need LIMIT 1, GroupID should be unique (primary) so it's controlled by the DB
    $sql['users']       = "DELETE FROM users WHERE Group=:groupID";

    // Start the transaction. PDO turns autocommit mode off depending on the driver, you don't need to implicitly say you want it off
    $pdo->beginTransaction();

    try
    {
        // Prepare the statements
        foreach($sql as $stmt_name => &$sql_command)
        {
            $stmt[$stmt_name] = $pdo->prepare($sql_command);
        }

        // Delete the privileges
        $stmt['privileges']->bindValue(':groupID', $_POST['groupID'], PDO::PARAM_INT);
        $stmt['privileges']->execute();

        // Delete the group
        $stmt['groups']->bindValue(":groupID", $_POST['groupID'], PDO::PARAM_INT);
        $stmt['groups']->execute();

        // Delete the user 
        $stmt['users']->bindParam(":groupID", $_POST['groupID'], PDO::PARAM_INT);
        $stmt['users']->execute();

        $pdo->commit();     
    }
    catch(PDOException $e)
    {
        $pdo->rollBack();

        // Report errors
    }    
}

【讨论】:

  • 哇,感谢您提供的 bindParam/Value 信息...我读过的所有 PDO 教程都使用了 bindParam。该代码看起来很棒,我会修改您的想法,很有意义。
  • 我在尝试删除该组中的用户时遇到错误。错误说在 Group=38 附近...其他两个查询仍然删除其他表中的其他行,似乎事务无法正常工作...
  • GROUP 是 SQL 保留字,因此您可能需要将其放在反引号中。但是最好重命名它,所以它不是保留字,而且你所有的表都将它命名为GroupID
【解决方案2】:

我不会准备和执行交易语句。我会使用 PDO::beginTransaction()PDO::commit()PDO::rollback()

如果出现错误,PDO::prepare() 和 PDO::execute() 返回 FALSE,否则如果您 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION),它们会抛出 PDOException。

在您的异常处理程序中,您应该检查PDO::errorInfo() 并报告错误的性质。最佳做法是记录原始错误信息,但给用户更友好的消息。

不要在 UI 中回显字面错误消息——这可能会让用户不恰当地了解您的 SQL 查询和架构。

【讨论】:

  • 是的,我刚刚了解了交易方法。我想用 if 语句控制它的全部原因是,对于我和我有限的经验来说,它更容易捕捉到特定的错误。我还读到由于各种原因,try 对于我正在做的事情并不理想。谢谢(你的)信息。很多东西要看!
  • 我刚刚仔细检查,setAttribute 选项设置为在我实例化 $dbo 对象时传递,所以我们很好..感谢反馈
【解决方案3】:

PDO 语句的execute() 在成功时返回 TRUE,在失败时返回 FALSE,因此您可以在 if 语句中测试前一个 execute() 的返回值。

$pdo_result = $dbs->execute();
if ($pdo_result) {
    // handle success
} else {
    // handle failure
    // you can get error info with $dbs->errorInfo();
}

也就是说,正如@Bill Kerwin 正确指出的那样(在他的回答中,我完全赞成,因为它完全正确),最好使用 PDO::beginTransaction()PDO::commit()PDO::rollback()

【讨论】:

  • 我怎么知道是什么导致了错误?我想让它响应这个问题。抱歉,第一笔交易对 PDO 来说还是有点新……感谢您的回复
  • $dbs->errorInfo() 有关其返回值的说明,请参阅php.net/manual/en/pdostatement.errorinfo.php 的文档。
猜你喜欢
  • 2015-11-15
  • 2013-03-25
  • 1970-01-01
  • 2012-04-26
  • 2017-10-24
  • 2013-05-15
  • 2016-09-30
  • 2012-11-03
相关资源
最近更新 更多