2011-02-08 4 views
2

Zend_Db_Adapter :: beginTransaction() 스택이 있습니까?

다음을 고려하십시오.

/** (Cas_Template_Tree::DeleteNode) 
* Deletes the given node from the tree, and all of it's children. 
* 
* @static 
* @throws Exception 
* @param Cas_Template_Node $node 
* @return void 
*/ 
public static function DeleteNode(Cas_Template_Node $node) 
{ 
    $table = new Cas_Table_Templates(); 
    $adapter = $table->getAdapter(); 
    $leftStr = $adapter->quoteIdentifier('Left'); 
    $rightStr = $adapter->quoteIdentifier('Right'); 
    try 
    { 
     $adapter->beginTransaction(); 
     $row = $table->find($node->GetId())->current(); 
     $dependantRowSelector = array(
      "$leftStr >= ?" => $row->Left, 
      "$rightStr <= ?" => $row->Right 
     ); 
     //Get the rows removed so that we can nuke the ACLs later. 
     $rowsToDelete = $table->fetchAll($dependantRowSelector)->toArray(); 
     //Delete the rows. 
     $table->delete($dependantRowSelector); 
     //Delete access control lists on those rows. 
     foreach ($rowsToDelete as $rowToDelete) 
     { 
      Cas_Acl::CreateExisting($rowToDelete['Acl'])->Delete(); 
     } 
     $left = (int)$row->Left; 
     $right = (int)$row->Right; 
     $difference = $right - $left + 1; 
     $table->update(array('Left' => new Zend_Db_Expr("$leftStr - $difference")), 
         array("$leftStr > ?" => $right)); 
     $table->update(array('Right' => new Zend_Db_Expr("$rightStr - $difference")), 
         array("$rightStr > ?" => $right)); 
     $adapter->commit(); 
    } 
    catch (Exception $ex) 
    { 
     $adapter->rollBack(); 
     throw $ex; 
    } 
} 

/** (Cas_Acl::Delete) 
* Removes this ACL (and all of its dependent access control entries) from the database. 
* @return void 
*/ 
public function Delete() 
{ 
    $aclTable = new Cas_Table_AccessControlLists(); 
    $aceTable = new Cas_Table_AccessControlEntries(); 
    $adapter = Zend_Db_Table_Abstract::getDefaultAdapter(); 
    $identifierName = $adapter->quoteIdentifier('Identifier'); 
    $aclName = $adapter->quoteIdentifier('Acl'); 
    try 
    { 
     $adapter->beginTransaction(); 
     $aceTable->delete(array("$aclName = ?" => $this->GetId())); 
     $aclTable->delete(array("$identifierName = ?" => $this->GetId())); 
    } 
    catch (Exception $ex) 
    { 
     $adapter->rollBack(); 
     throw $ex; 
    } 
} 

그렇지 않으면 조작이 어둡지 않을 수 있기 때.에 둘 다 트랜잭션 작동이 필요합니다. 원래 DeleteNode 메소드는 Cas_Acl::Delete()를 호출합니다. Cas_Acl::Delete() 역시 트랜잭션 블록 내부에서 자체 실행을 시도합니다. 이상적으로, Zend_Db는이 경우를 인식 할만큼 똑똑 할 것이며,이 특정 호출에 대해 Cas_Acl::Delete 내부에서 begin transaction 및 commit/rollback 호출을 무시하십시오.

위의 코드가 안전할까요? 어떤 방식 으로든 크게 개선 될 수 있습니까?

+0

'Delete()'를 직접 호출하는 경우가 있습니까? 또는'DeleteNode()'내에서만 호출 되는가? – singles

+0

@singles : Yes - ACL을 삭제해야 할 때마다'Cas_Acl :: Delete'가 호출됩니다. 그리고이 앱이 관리하는 대부분의 모든 객체에 ACL이 첨부되어 있습니다. 이러한 객체 중 일부는 트랜잭션을 사용하지만 다른 객체는 필요하지 않습니다. –

+0

당신의 코드가 tintin http://omg.wthax.org/haddock.jpg의 시트 위 또는 아래에있는 haddock의 수염처럼 "$ adapter-> beginTransaction();"이라고 생각합니다. – regilero

답변

1

을 : 할 당신이 수도

것은 트랜잭션을 사용 여부 및 DeleteNode()false PARAM으로 호출할지 여부를 결정합니다 Delete() 방법으로 두 번째 PARAM를 추가입니다 이 로직을 추가하십시오 (아직 시작되지 않은 경우 트랜잭션 시작).

이 유형의 객체 (위임?)가 매우 유용합니다. 나는 또한 올바른 액세스와 특정 DB 연결을 사용하는 데 사용합니다. 트랜잭션 내의 모든 쓰기 및 읽기 쿼리는이 TransactionManager 객체를 사용합니다.

roolback에 대한 호출은 컨트롤러에서 수행되지만 TransactionManager를 통해서도 수행됩니다. 실제로 수행해야하는지 여부를 감지합니다. 롤백을 두 번 호출하면 기존 데이터베이스의 대부분이 울려지기 때문에 매우 유용합니다.

마지막으로 일반 규칙 을 사용하여 트랜잭션 제어 코드를 코드에서 (컨트롤러에만 있음)에서 매우 "상위 수준"으로 설정하고 더 추상적 인 하위 수준 개체에서는 사용하지 않도록 설정합니다. 따라서 일반적으로 대부분의 객체에 대해 Delete -() 액션을 호출 할 수 있습니다.이 객체가 트랜잭션을 처리하지 않고 WE를 수행해야한다는 것을 알고 있습니다. 그러나 효과적으로, 이것은 일반적인 규칙 일 뿐이며 때로는 트랜잭션을 포함하고 TransactionManagers는 문제를 숨기고 (그리고 stacktrace를 사용하여 트랜잭션을 기록 할 수 있습니다) 도움이됩니다.

+0

+1 - 나는 아마도 모든 트랜잭션 로직을 컨트롤러로 옮길 것입니다. (나는 "트랜잭션 관리자"라는 생각을 좋아하지 않는다. 왜냐하면 PHP는 결정 론적 인 파괴를 가지고 있지 않기 때문에 RAII는 문제가되지 않는다."manager"라는 단어를 포함하는 모든 클래스는 코드 냄새이기 때문입니다. –

+0

@Billy ONeal : register_shutdown 함수가 있습니다. 지속적인 연결을 사용하지 않으면 문제가 발생하면 트랜잭션이 시간 초과되므로 결정적 파괴가 문제가되지 않습니다. 또한 원할 경우 TransactionAdapter라고 할 수 있으므로 이름을 관리하거나 처리 할 필요가 없으므로 실제 패턴을 사용할 수 있습니다. – regilero

+0

@regilero : 이름이 적고 클래스가 실제로하는 일이 더 많습니다. "관리자"와 같은 단어는 "이 수업에서 수행해야 할 작업을 정확히 모르기 때문에 훌륭한 단어입니다."- 일반적으로 SRP 위반을 나타냅니다. (부인 : 저는 여기서 유리 집에 있습니다. 위의 첫 번째 방법은'Cas_Template_Locator'에 있습니다. –

2

AFAIK Zend_Db은 중첩 트랜잭션을 인식 할 수 없습니다. 코드를보세요.

public function beginTransaction() 
{ 
    $this->_connect(); 
    $q = $this->_profiler->queryStart('begin', Zend_Db_Profiler::TRANSACTION); 
    $this->_beginTransaction(); 
    $this->_profiler->queryEnd($q); 
    return $this; 
} 

어떤 코드가 다른 트랜잭션을 인식 여기에 없다 (하지만 어쩌면 프로파일 러는 같은 일을하는 데 사용될 수)와 _beginTransaction는 PDO의 beginTransaction에 의존한다. 내가 정의 의 transactionManager 객체를 사용

//RB called as Cas_Acl::CreateExisting($rowToDelete['Acl'])->Delete(false); 
public function Delete($useTransaction = true) 
{ 
    $aclTable = new Cas_Table_AccessControlLists(); 
    $aceTable = new Cas_Table_AccessControlEntries(); 
    $adapter = Zend_Db_Table_Abstract::getDefaultAdapter(); 
    $identifierName = $adapter->quoteIdentifier('Identifier'); 
    $aclName = $adapter->quoteIdentifier('Acl'); 
    try 
    { 
     if ($useTransaction === true) { 
      $adapter->beginTransaction(); 
     } 
     $aceTable->delete(array("$aclName = ?" => $this->GetId())); 
     $aclTable->delete(array("$identifierName = ?" => $this->GetId())); 
     //BTW isn't commit() should be called here? 
    } 
    catch (Exception $ex) 
    { 
     if ($useTransaction === true) { 
      $adapter->rollBack(); 
     } 
     throw $ex; 
    } 
} 
+0

여기서는 부울 대신 전략 객체를 사용합니다 (다형성> 조건문). 그렇지 않으면 좋은 대답 (+1) –

+0

전략 패턴도 좋은 옵션입니다 :) – singles

관련 문제