Yii2中使用Optimistic Locking实现删除操作遇version不更新问题求助
Yii2乐观锁删除操作中version始终为0的问题解决
问题描述
开发Yii2 Todo应用时,使用乐观锁实现删除功能,但调试发现生成的SQL语句中version始终为0,例如:
DELETE FROM `records` WHERE (`id`=100) AND (`version`=0)
无法正确触发乐观锁的版本校验逻辑。
问题根源
- 模型行为配置错误:原模型中
OptimisticLockBehavior的value直接绑定$this->permitValue,但行为初始化时该属性值为null,导致乐观锁默认使用0作为版本值。 - 冗余属性与逻辑:自定义的
permitValue属性完全多余,乐观锁本身可以直接使用模型的version字段。 - 控制器逻辑不当:提前用
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
相关产品推荐
相关产品推荐

