【问题标题】:Yii ActiveRecord - Is there way to update only dirty attributes?Yii ActiveRecord - 有没有办法只更新脏属性?
【发布时间】: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并没有解决问题

标签: php database activerecord yii process


【解决方案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() 方法会更新所有属性。
  • 我的意思是,我的方法很危险;)看看我写的,它会让你知道如何处理它。虽然这是一个粗略的想法
猜你喜欢
  • 2011-04-15
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 2014-12-16
  • 2012-09-25
  • 2015-04-25
  • 1970-01-01
相关资源
最近更新 更多