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:具有不同查询的 ActiveRecord

Yii2 ActiveRecord save失败

Yii2 错误 - 找不到类 'yii\mongodb\ActiveRecord'

yii2 ActiveRecord多表关联以及多表关联搜索的实现

yii2之ActiveRecord 模型

yii2 使用 ActiveRecord 批量插入