Yii ActiveRecord - 有没有办法只更新脏属性?

时间:2014-01-29 20:18:15

标签: php database activerecord yii process

在我的应用程序中,我有两个几乎同时运行的进程并更新相同的AR模型。

当看起来有些过程没有完成时,我正面临着这个错误,但是单独调试它们没有错误。

然后我明白问题可能发生在下一个案例中:

  • 流程A选择第X行
  • 流程B选择第X行
  • 流程B更新第X行
  • 进程A更新第X行

在描述的情况下,进程A将覆盖B写入的所有进程。

B和A都会更新不同的属性。

有没有办法避免这种覆盖?是否有一些机制使AR更新'Dirty'属性而不是所有模型属性?

请不要在不使用AR的情况下向我解释解决方案。我明白了。但我想知道是否有一些解决方案可以让我使用AR正确地进行所需的更新。

感谢。

2 个答案:

答案 0 :(得分:2)

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();
}

答案 1 :(得分:2)

我不知道会有什么结果,但这是一个非常危险的想法,请阅读thread

  • 创建另一个用于使用相应模型进行锁定的表
  • mylocks(object,object_type,lock_type),使其过于通用
  • 例如记录
  • mylocks( '后', '表', '写')

    `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

注意

不是生产准备好的代码,没有测试可能带有缺陷