【问题标题】:Wrapping class methods that use transactions包装使用事务的类方法
【发布时间】:2016-02-19 15:08:21
【问题描述】:

我有多个类方法,如果抛出异常,每个方法都使用事务和回滚。下面的基本示例。

class TaskMapper
{
    private $dblayer;

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

    public function save(Task $task)
    {
        try {
            $this->dblayer->beginTransaction();

            $stmt = ... // query/queries here
            $stmt->execute();

            $stmt2 = ... // query/queries here
            $stmt2->execute();

            $this->dblayer->commit();

        } catch (\PDOException $e) {

            $this->dblayer->rollBack();
            // deal with exception here
        }


    }

    public function editField($id, $field, $value)
    {
        try {
            $this->dblayer->beginTransaction();

            $stmt = ... // query/queries here
            $stmt->execute();

            $stmt2 = ... // query/queries here
            $stmt2->execute();

            $stmt->execute();

            $this->dblayer->commit();

        } catch (\PDOException $e) {

            $this->dblayer->rollBack();
            // deal with exception here
        }
    }

}

现在,我正在为 cron 作业编写一个脚本,该脚本利用多个类方法,其中包含与上述类似的代码 - 具有事务和回滚以及异常。

例如:

if (date('j') == '1') {
    // reset monthly count column
    $task->editField(17, 'monthly_count', '0');
}
$task->save($task);

这是一个非常精简的示例,但我想知道是否可以将所有方法调用包装在 try/catch 块内的单个事务中,并在 catch 内回滚?那么将上面的代码包装在一个带有事务的 try/catch 中并在 catch 中回滚?

如果不是,那么处理这种情况的最佳方法是什么,如果一个方法调用失败,则必须回滚其他方法。

提前致谢。

【问题讨论】:

  • 对我来说,save() 方法的存在意味着所有数据库事务都将从该调用中处理。到目前为止,更改仅在 php 对象级别完成。 save() 的作用是持久化更改,因此会将所有查询包装到单个事务中。

标签: php mysql pdo transactions


【解决方案1】:

我宁愿添加一个答案,因为 cmets 太短,无法解释。

基本上,您有两种方法可以在应用程序的单个事务中包含多个操作。

  1. 调用启动事务的方法。每次后续方法调用,属性更改都会导致数据库发生更改。这些操作期间的任何错误都将导致回滚。最后你调用另一个提交整个事务的方法。

  2. 每次方法调用,属性改变只改变php对象的状态,所以数据库没有改变。在您对所有更改感到满意后,您调用一个方法,将所有更改保存到数据库。在这个调用中也执行回滚/提交。

Mysql 不支持嵌套事务,所以要么你使用第一种方法与PDO:inTransaction 结合使用@YourCommonSense 也建议在调用editField() 之前启动一个事务,或者你必须从editField() 和类似的调用,只使用 save()。

【讨论】:

  • 感谢您 - 将尝试这些并标记为正确,但可能还有更多问题!
【解决方案2】:

我自己没用过,但是有一个PDO::inTransaction的方法,可以用来查看是否有更高级别的事务启动。

所以你可以改成

if ( !$this->dblayer->inTransaction() ) {
    $this->dblayer->beginTransaction();
}

对于提交也是如此。

这将让您决定是否必须使用“本地”事务,从而可以使用“全局”事务将所有这些方法包装在一起。

【讨论】:

  • 这不是一个坏主意,但是,OP 中没有迹象表明任务将启动并保持包含事务的打开状态。因此 inTransaction() 将返回 false。
  • @Shadow 你能解释一下你在这里的意思 - 关于保持交易开放吗?谢谢
  • inTransaction() 如果有未结交易,则返回 true。但是,您的 save() 和 editField() 方法都有自己的启动事务和提交。 inTransaction() 唯一有用的方法是,如果您在调用 start() 或 editField() 之前启动数据库事务之前
  • @Shadow 你的意思是在类文件中还是在 cron 脚本中?
  • @CIvemy 查看我刚刚发布的答案
猜你喜欢
  • 2019-01-09
  • 1970-01-01
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-03
  • 1970-01-01
  • 2014-06-06
  • 1970-01-01
相关资源
最近更新 更多