You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yii2中使用Optimistic Locking实现删除操作遇version不更新问题求助

Yii2乐观锁删除操作中version始终为0的问题解决

问题描述

开发Yii2 Todo应用时,使用乐观锁实现删除功能,但调试发现生成的SQL语句中version始终为0,例如:

DELETE FROM `records` WHERE (`id`=100) AND (`version`=0)

无法正确触发乐观锁的版本校验逻辑。

问题根源

  1. 模型行为配置错误:原模型中OptimisticLockBehavior的value直接绑定$this->permitValue,但行为初始化时该属性值为null,导致乐观锁默认使用0作为版本值。
  2. 冗余属性与逻辑:自定义的permitValue属性完全多余,乐观锁本身可以直接使用模型的version字段。
  3. 控制器逻辑不当:提前用version作为查询条件过滤模型,无法触发乐观锁的异常机制,且设置permitValue的时机无法覆盖行为初始化时的错误配置。

修正方案

1. 修正模型代码

移除冗余的permitValue属性,简化乐观锁行为配置:

<?php
namespace app\models;

use yii\behaviors\OptimisticLockBehavior;
use yii\db\ActiveRecord;

class RecordModel extends ActiveRecord
{
    public static function tableName()
    {
        return 'records';
    }

    public function behaviors()
    {
        return [
            // 直接注册行为,无需额外配置value,自动使用optimisticLock()返回的字段
            OptimisticLockBehavior::class,
        ];
    }

    public function optimisticLock()
    {
        return 'version';
    }

    public function rules()
    {
        return [
            [ ['title', 'priority'], 'required'],
            [ ['title', 'priority'], 'trim'],
        ];
    }
}

2. 修正控制器代码

调整删除逻辑,直接使用模型的version字段触发乐观锁校验:

use yii\db\StaleObjectException;
use yii\web\NotFoundHttpException;
use yii\base\InvalidCallException;

public function actionDelete($id, $version)
{
    $model = RecordModel::findOne($id);
    if (!$model) {
        throw new NotFoundHttpException('目标记录不存在');
    }

    // 将请求传来的版本赋值给模型,触发乐观锁校验
    $model->version = $version;

    try {
        $model->delete();
        Yii::$app->session->setFlash('success', '删除成功!');
        return $this->redirect('/');
    } catch (StaleObjectException $e) {
        Yii::$app->session->setFlash('error', '冲突:该记录已被其他用户修改,删除操作无法执行,请刷新页面后重试。');
        // 获取最新版本的记录返回给编辑页
        $model = RecordModel::findOne($id);
        return $this->render('edit', [
            'model' => $model,
        ]);
    } catch (InvalidCallException $e) {
        Yii::$app->session->setFlash('error', '删除操作失败:' . $e->getMessage());
        return $this->redirect(['edit', 'id' => $id]);
    }
}

3. 视图代码无需修改

原视图中传递version参数的逻辑是正确的,保持即可:

<?= Html::a('Delete', ['delete', 'id' => $model->id, 'version' => $model->version], ['class' => 'btn btn-danger right'])?>

关键说明

  • 确保数据库records表的version字段为整数类型,默认值设为0,Yii会在每次更新/删除时自动递增该字段值。
  • 乐观锁的核心逻辑是:执行删除时,SQL会带上version = [当前请求的版本]的条件,若数据库中该记录的version已变更,则删除行数为0,触发StaleObjectException。

内容的提问来源于stack exchange,提问作者Azizxon Zufarov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 01:52:58