Yii ActiveRecord - 有没有办法只更新脏属性?
Posted
技术标签:
【中文标题】Yii ActiveRecord - 有没有办法只更新脏属性?【英文标题】:Yii ActiveRecord - Is there way to update only dirty attributes? 【发布时间】:2014-01-29 20:18:15 【问题描述】:在我的应用程序中,我有 2 个进程几乎同时运行并更新相同的 AR 模型。
当其中一些进程看起来没有完成时,我遇到了这个错误,但单独调试它们没有错误。
然后我明白问题可能发生在下一个案例中:
进程 A 选择行 X 进程 B 选择行 X 进程 B 更新第 X 行 进程 A 更新第 X 行在上述情况下,进程 A 将覆盖进程 B 写入的所有内容。
B 和 A 都更新不同的属性。
有没有办法避免这种覆盖?是否有某种机制可以让 AR 只更新“脏”属性而不是所有模型属性?
请不要在不使用 AR 的情况下向我解释解决方案。我明白了。但我想知道是否有一些解决方案可以让我仍然使用 AR 正确地进行所需的更新。
谢谢。
【问题讨论】:
你不使用 AR 的交易吗? 请描述过程 @AliMasudianPour 当然,每个进程都使用事务,因为它们都执行与与其他 Web 服务拨号相关的复杂工作。但是TR并没有解决问题 【参考方案1】:YourTable::model()->updateByPk($id, array(
'field1' => NewVal1,
'field2' => NewVal2,
'field3' => NewVal3
));
并利用交易:
$transaction=Yii::app()->db->beginTransaction();
try
//.... SQL executions OR model save()
$transaction->commit();
catch(Exception $e) // an exception is raised if a query fails
$transaction->rollback();
【讨论】:
谢谢,我知道什么是交易))我非常喜欢你的想法。【参考方案2】:我不知道这会如何发生,但这是一个非常危险的想法,请阅读thread
创建另一个表以锁定各个模型 mylocks(object, object_type ,lock_type) 太笼统了 例如记录是mylocks('post','table','write')
`class Post 扩展 ActiveRecord
public static $dirtyData=array();
protected $semaphore=false;
//if its locked true, else false
protected function hasSemaphore()
$c = new CDbCriteria;
$c->compare('object',$this->getTableName());
$c->compare('object_type','table');
$lock=MyLocks::model()->find($criteria)
return $lock!=null;
//
public function setSemaphore()
if($this->semaphore==true)
return true ;
if($this->hasSemaphore())
Yii::app()->db->createCommand('LOCK TABLE '.$this->getTableName().' WRITE;')->execute();
//insert a record to MyLocks
//insert into mylocks(object,object_type,lock_type)
//values ('post' ,'table','WRITE');
$this->semaphore=true;
return true;
$this->semaphore=false;
return false;
protected function mergeDirtyData()
//as I am holding write lock i should collect all dirty
// data from other models to save it ....
protected function releaseSemaphore()
if($this->semaphore)
//delete matching from mylocks table -- sorry I am lazy
Yii::app()->db->createCommand('UNLOCK TABLES;')->execute();
$this->semaphore=false;
$this->mergeDirtyData()
return true;
return false;
....
public function beforeSave()
//if I am holding lock - release it
if(!$this->releaseSemaphore())
//probably someone else is holding
if($this->hasSemaphore())
//set values to dirtyData
//self::dirtyData[]=array(attrA=>valueA,....);
return false; // disable saving
return parent::beforeSave();
'
这将是您的操作流程
//进程A
$postA=Post::model();
...
$postA->setSemaphore();
...更新一些字段
//进程B
$postB=Post::model();
...更新$postB的一些字段
$postB->update();
$postA->update()
其他可能未处理的情况
当进程一存在读锁时,您将无法插入记录,因此您有 为了解决这个问题,在插入一个进程 B 的同时获得锁释放 它会再次重置那个锁(借用它并把它还给它)……就像那样
我没有处理过dirtyData。这个想法是只有一个模型会正确地将数据写入db 设置信号量 ==lock
注意
不是生产就绪的代码,没有经过测试,因此可能存在缺陷
【讨论】:
没什么危险的。完全没有。只想更新同一行的不同列。但 AR 原生 save() 方法会更新所有属性。 我的意思是,我的方法很危险;)看看我写的,它会让你知道如何处理它。虽然这是一个粗略的想法以上是关于Yii ActiveRecord - 有没有办法只更新脏属性?的主要内容,如果未能解决你的问题,请参考以下文章
Yii2 错误 - 找不到类 'yii\mongodb\ActiveRecord'