当前位置:首页 > PHP > 正文内容

tp orm事务提交未执行的教训和总结

高老师7年前 (2019-12-31)PHP2054

最近在项目中处理一个关于商品数据重复需要删除多余的商品记录,但是删除一条商品必然要把关联的其他表商品的id和其他商品信息更换为正确的,删除一个商品记录,同时要去修改100多张表的关联商品数据,在项目中引用了tp orm 1.2版本,由于项目是php5.6版本,没法使用最新orm,在代码中每处理1个商品则开启1个事务,相当于循环的事务,下面的代码做个简单的演示(非真实):

$list = Db::table('member')->select();

//循环事务
foreach ($list as $item)
{
    try
    {
        //开启事务
        Db::startTrans();

        //部分数据不需要处理
        if ($item['sex'] == 2)
        {
            continue;
        }

        //更新数据
        $isUpdate = Db::table('member')->where(['id' => $item['id']])->update([
            'name' => 'chen'
        ]);
        if (!$isUpdate)
        {
            throw new Exception('更新数据失败');
        }

        //更新其他表(假装这里写了代码)
        
        // 提交事务
        Db::commit();
    }
    catch (Exception $e)
    {
        Db::rollback();
        echo '异常事务回滚:' . $e->getMessage() . PHP_EOL;
    }
}

echo '执行完成'.PHP_EOL;

上面的代码中每循环一条数据开启1个事务,执行完成提交事务,异常会回滚。但是在预发布环境执行出现前面的数据可以执行,后面的很多数据未执行。于是我在代码中加了文件日志,保存了生成的sql,甚至在提交事务后面加了日志,确保执行到提交事务后面了。最终结果是程序输出了"执行完成",且执行过程中未报错,无任何异常抛出,包括未捕捉的异常。由于我们每次提交代码都需要找运维,本来是想揪出原因,但是怕麻烦运维的小伙伴,于是简单粗暴的使用了tp的事务闭包函数。tp会自动处理错误,然后你可以捕捉tp这个方法抛出的异常来记录日志即可,不需要自己处理事务。

Db::transaction(function (){
    //逻辑写在这里
});

个人比较较真,一直找不到原因非常不爽,于是抽空下班后完整模拟了下终于发现问题。原来tp在开启事务会在orm中记录事务的次数+1,事务提交和事务回滚都会将事务的次数进行-1操作。例如:

/**
 * 启动事务
 * @access public
 * @return void
 * @throws \PDOException
 * @throws \Exception
 */
public function startTrans()
{
    $this->initConnect(true);
    if (!$this->linkID) {
        return false;
    }

    ++$this->transTimes;

    try {
        if (1 == $this->transTimes) {
            $this->linkID->beginTransaction();
        } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
            $this->linkID->exec(
                $this->parseSavepoint('trans' . $this->transTimes)
            );
        }
    } catch (\Exception $e) {
        if ($this->isBreak($e)) {
            --$this->transTimes;
            return $this->close()->startTrans();
        }
        throw $e;
    }
}

/**
 * 事务回滚
 * @access public
 * @return void
 * @throws PDOException
 */
public function rollback()
{
    $this->initConnect(true);

    if (1 == $this->transTimes) {
        $this->linkID->rollBack();
    } elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
        $this->linkID->exec(
            $this->parseSavepointRollBack('trans' . $this->transTimes)
        );
    }

    $this->transTimes = max(0, $this->transTimes - 1);
}

/**
 * 用于非自动提交状态下面的查询提交
 * @access public
 * @return void
 * @throws PDOException
 */
public function commit()
{
    $this->initConnect(true);

    if (1 == $this->transTimes) {
        $this->linkID->commit();
    }

    --$this->transTimes;
}

通过上面commit方法我们可以看到当事务次数只有1的时候才会执行事务提交,上面我们的业务代码直接continue导致下次执行的时候事务的次数一直在递增,因为tp orm是静态的,因为只要continue就会导致普通commit事务执行失败,这块tp文档未说明,且在方法中未作异常处理,开发者特别容易进坑。

扫描二维码推送至手机访问。

版权声明:本文由高久峰个人博客发布,如需转载请注明出处。

本文链接:https://blog.20230611.cn/post/114.html

分享给朋友:

“tp orm事务提交未执行的教训和总结” 的相关文章

PHP函数赋值给一个变量的另类用法

PHP函数赋值给一个变量的另类用法

<?php   function  go($str) { echo'I\'m '.$str;  }   $goto='go';   $goto('gaojiufeng'...

php 数组转换xml,php 数组转成xml,php数组转xml 函数

php 数组转换xml,php 数组转成xml,php数组转xml 函数

源码:特别适用于微信支付中通知微信支付网关function array2xml($arr, $level = 1) { $s = $level == 1 ? "<xml&g...

php trait的使用

php trait的使用

PHP不像net支持多继承,自身只支持单继承,为了解决这个问题,php出了Trait这个特性,减少单继承语言的限制。并且能让代码复用率更高。说白了就是一个对象的属性和方法扩展工具一样。例如:trait exts {     public f...

【一】PHP多进程学习-简单创建父子进程.理解父子进程运行流程

【一】PHP多进程学习-简单创建父子进程.理解父子进程运行流程

(1).学习的目标:学会创建父子进程,并且能够区分当前进程是父还是子;了解父进程执行过程,子进程执行过程;能够用多进程执行任务(2).相关函数学习:    (2.1)pcntl_fork()执行时:       &nbs...

thinkphp3定时任务,tp定时任务,thinkphp定时任务

thinkphp3定时任务,tp定时任务,thinkphp定时任务

本教程使用的定时任务基于EasyTak,EasyTask官方文档:https://gitee.com/392223903/EasyTask由于tp3.2.x官方开发未考虑命令行支持和绝对路径开发的标准,因此我编写了一个支持的类来运行。1.在tp3.2.3根目录下安装easytaskcomposer&...

 thinkphp5定时任务,tp5定时任务,thinkphp定时任务,php定时任务,php定时器

thinkphp5定时任务,tp5定时任务,thinkphp定时任务,php定时任务,php定时器

本教程使用的定时任务基于EasyTak,EasyTask官方文档:https://gitee.com/392223903/EasyTask(1).安装tp5.0或者5.1composer create-project topthink/think=5.0.* tp5&n...