2012-11-17 1 views
5

조금 혼란 스럽습니다. 필자는 다이렉트 메일 서비스에서 PHP RedBean을 ORM으로 적극적으로 사용하고 있습니다. 고유 키 제약 조건 (예 : subscriber_id, delivery_id)과이 테이블에 데이터를 쓰는 두 개의 스크립트가있는 테이블이 있습니다. 삽입되는 소스 코드 나 업데이트 테이블이 있습니다 :PHP RedBean 스토어 빈이 없으면 하나

public static function addOpenPrecedent($nSubscriberId, $nDeliveryId) 
{ 
    $oOpenStatBean = \R::findOrDispense('open_stat', 'delivery_id = :did AND subscriber_id = :sid', array(':did' => $nDeliveryId, ':sid' => $nSubscriberId)); 

    $oOpenStatBean = array_values($oOpenStatBean); 
    if (1 !== count($oOpenStatBean)) { 
     throw new ModelOpenStatException(
      "Ошибка при обновлении статистики открытий: пара (delivery_id, 
      subscriber_id) не является уникальной: ($nDeliveryId, $nSubscriberId)."); 
    } 

    $oOpenStatBean = $oOpenStatBean[0]; 
    if (!empty($oOpenStatBean->last_add_dt)) { 
     $oOpenStatBean->precedent++; 
    } else { 
     $oOpenStatBean->delivery_id = $nDeliveryId; 
     $oOpenStatBean->subscriber_id = $nSubscriberId; 
    } 

    $oOpenStatBean->last_add_dt = time('Y-m-d H:i:s'); 
    \R::store($oOpenStatBean); 
} 

그것은 모두 두 개의 스크립트에서 호출됩니다. 그리고 경쟁 조건이 발생하기 때문에이 테이블에 대한 손상 고유 한 제약 조건과 관련된 문제가 정기적으로 발생합니다. SQL "INSERT on duplicate key update"기능에 대해 알고 있습니다. 하지만 어떻게하면 ORM을 사용하여 동일한 결과를 얻을 수 있습니까?

+2

당신이하려는 것은 "업서 트"라고합니다. 인터넷 검색을 시도해보십시오. 나는 이것에 대한 토론을 발견했다 : https://github.com/gabordemooij/redbean/issues/160 –

답변

3

현재, 내가 알고 있다는 Redbean는 코멘트 위 Redbean의 개발자가 오염 것 비즈니스 로직 일 수 upsert을 고려 있음을 나타냅니다에서 인용이의 논의로

INSERT ON DUPLICATE KEY UPDATE 

를 발행하지 않습니다 ORM의 중간 단계. 즉, Documentation에 대한 사용자 지정 쿼리 작성기 또는 플러그 인을 사용하여 Redbean을 확장하는 것이 가장 가능성이 높습니다. 나는 아래의 메소드가 ORM의 내부와 플러그인을 망치지 않고 쉽게이 동작을 달성하기 때문에 이것을 시도하지는 않았다. 그러나 트랜잭션과 모델과 몇 가지 추가 쿼리를 사용해야한다.

기본적으로 R :: store() 호출 전에 R :: transaction() 또는 R :: begin()을 사용하여 트랜잭션을 시작하십시오. 그런 다음 "FUSE"d 모델에서 "업데이트"FUSE 메소드를 사용하여 중복을 확인하고 기존 행을 잠그는 동안 기존 ID를 검색하는 쿼리를 실행합니다 (예 : SELECT FOR UPDATE). id가 반환되지 않으면 정상적인 모델 유효성 검사 (또는 그 부족)를 평소처럼 계속하고 반환하십시오. id가 발견되면, 간단히 $ this-> bean-> id를 반환 값으로 설정하면 Redbean은 INSERT가 아닌 UPDATE됩니다. 그래서,이 같은 모델 : 그런 다음 R을 수정하는 것

class Model_OpenStat extends RedBean_SimpleModel{ 
    function update(){ 
    $sql = 'SELECT * FROM `open_stat` WHERE `delivery_id`=? AND 'subscriber_id'=? LIMIT 1 FOR UPDATE'; 
    $args = array($this->bean->deliver_id, $this->bean->subscriber_id); 
    $dupRow = R::getRow($sql, $args); 
    if(is_array($dupRow) && isset($dupRow['id'])){ 
     foreach($this->bean->getProperties() as $property => $value){ 
      #set your criteria here for which fields 
      #should be from the one in the database and which should come from this copy 
      #this version simply takes all unset values in the current and sets them 
      #from the one in the database 
      if(!isset($value) && isset($dupRow[$property])) 
      $this->bean->$property = $dupRow[$property]; 
     } 
     $this->bean->id = $dupId['id']; #set id to the duplicates id 
    } 
    return true; 
    } 
} 

: 저장()과 같이 전화 :

\R::begin(); 
\R::store($oOpenStatBean); 
\R::commit(); 

또는

\R::transaction(function() use ($oOpenStatBean){ R::store($oOpenStatBean); }); 

거래는 원인이됩니다 "FOR UPDATE"절은 찾은 행을 잠 그거나, 행이없는 경우 색인에 새 행이있는 위치를 잠 그어 동시성 문제가 발생하지 않도록합니다.

이제는 한 사용자의 레코드 업데이트를 해결하지 못해 다른 레코드를 손 상시킬 수는 있지만 완전히 다른 주제입니다.

+0

통합 된 ORM 지원과 같이 너무 우아한 해결책은 아니다. –

+0

불행하게도, 언급 된 토론과 같이, 진정한 upsert 기능은 비즈니스 논리로 간주되어 Redbean 코어에 통합되지 않습니다. 그러나 이런 종류의 것을 orm으로 옮길 수있는 커스텀 질의 작가 또는 플러그인을 작성할 수 있습니다. 이들에 대한 정보는 [documentation] (http://www.redbeanphp.com/plugins/create_your_own)에 있습니다. –

+0

감사합니다.하지만 1 년 전에이 질문을했습니다. –

관련 문제