每个语句的PDO异常VS.一次交易?

时间:2014-04-08 19:57:46

标签: php exception pdo

我即将在我的php脚本中实现事务,并且我正在做一些测试以帮助自己准确理解它们的工作方式。我有以下代码段:

try{
    $db->beginTransaction();

    $update = "UPDATE persons SET first_name = 'Adam' WHERE person_id = 4";
    $stmt = $db->exec($update);


    $select = "SELECT person_id, column_that_doesnt_exist FROM persons";

    try{
        $stmt = $db->prepare($select);
        $stmt->execute();
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
        echo json_encode('success');
    } catch (PDOException $e) {
        echo 'execute failure';
    }

    echo ' code gets here';

    $db->commit();
} catch(PDOException $e){
    $db->rollBack();
    echo json_encode('commit failure');
}

输出:

execute failure code gets here

第4个人的名字更新为Adam。

现在我很确定它已经被提交,因为第二个查询从未真正失败,因为它从未实际执行过,因为prepare是失败点。

如果最后一个PDOException抛出了catch,那将是很好的,因为一个人被扔进了内部" try但我可以解决这个问题。

现在,如果我拿出"内部" try并拥有此代码:

try{
        $db->beginTransaction();

        $update = "UPDATE persons SET first_name = 'Adam' WHERE person_id = 4";
        $stmt = $db->exec($update);


        $select = "SELECT person_id, column_that_doesnt_exist FROM persons";


            $stmt = $db->prepare($select);
            $stmt->execute();
            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);


        $db->commit();
    } catch(PDOException $e){
        $db->rollBack();
        echo json_encode('commit failure');
    }

提交失败,db回滚并按预期输出commit failure

所以在生产中,我应该 NOT 将每个单独的语句包装在try-catch中,而是将我的所有语句都放在一个大的try块(事务)中,然后最后抓住commit例外?这对我来说似乎不对......而且同样不会给你很多关于哪个陈述失败的信息......

2 个答案:

答案 0 :(得分:1)

使用内部try..catch块捕获异常,而无需重新抛出它。这意味着处理该异常,并且代码继续,好像什么都没有出错,提交事务。

通常这不是理想的解决方案,因为事务主要用于将多个数据修改语句组合成原子。通过使用事务,如果一切顺利,您可以提交所有内容,或者在出现问题时回滚所有内容。

此外,由于在启动事务本身失败时无法提交或回滚,因此您应该将其从异常处理中拉出来。所以适当的结构将是:

StartTransaction;
try {
  ModifyData;
  CommitTransaction;
}
catch {
  RollbackTransaction;
  // Log/mail/show/ignore error
}

如果要继续插入记录,即使插入其中一个记录失败,也不需要进行交易。

如果您想获得有关哪个项目失败的具体信息,您可以重新抛出异常或抛出一个新异常:

StartTransaction;
try {

  foreach ($persons as $person) {
    try {
      ModifyPerson($person);
    }
    catch {
      throw new Exception("Updating person {$person->name} failed");
    }
  }

  CommitTransaction;
}
catch {
  RollbackTransaction;
  // Log/mail/show/ignore error
}

通过(重新)从内部异常处理程序抛出异常,您直接跳转到外部异常处理程序,终止循环,并绕过提交。

答案 1 :(得分:0)

删除内部的try catch块。

使用echo $ e-> getMessage()获取错误信息; //注意不要在关键点或功能(对用户)中显示消息,因为此消息可能会显示数据库信息甚至是密码。

在这种特定情况下,您无法按照自己的方式获取更多详细信息。为什么一个人失败而另一个人失败? 但是做一些事情,比如首先获取所有行,然后逐个更新1(这里有一个内部的try catch或保存当前行),以获得给出错误的特定行,我认为更多。