2011-02-08 69 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(),它也尝试在事务块内执行自己。理想情况下,Zend_Db足够聪明以识别这种情况,对于这个特定的调用忽略Cas_Acl::Delete内的开始事务和提交/回滚调用。

上面的代码是否安全?它能以任何方式显着改善吗?

+0

你有时直接调用Delete()吗?还是只在`DeleteNode()`中调用? – singles 2011-02-08 07:22:32

+0

@singles:是 - 只要需要删除ACL,就会调用“Cas_Acl :: Delete”。还有一个ACL附加到这个应用程序管理的大多数对象。其中一些对象使用交易,而另一些则不需要。 – 2011-02-08 08:38:14

+0

你的代码提出了我的问题,就像tintin http://omg.wthax.org/haddock.jpg中的haddock的胡子一样,我认为“$ adapter-> beginTransaction();”应该在“try {} catch”之前设置,而不是在 – regilero 2011-02-08 12:49:46

回答

1

我使用自定义transactionManager的对象和添加此逻辑(如果尚未开始,则启动事务)。

我发现这种类型的对象(Delegation?)非常有用。我也使用它来使用具有正确访问权限的特定数据库连接。一个事务中的所有写入和读取查询都使用这个TransactionManager对象。

调用roolback是在控制器中完成的,但也通过TransactionManager来检测它是否真的应该这样做,并且它也非常有用,因为调用Rollback两次将使大多数现有数据库都哭泣。

最后,我们尝试使用一个通用的规则,使交易控制代码非常“高级别”的代码(仅限控制器),而不是在更抽象的分层次的对象。所以我们通常可以在大多数对象上调用任何 - > Delete()动作,并且我们知道这个对象不是试图处理事务,而是我们应该这样做。但是,实际上,这只是一个通用规则,有时会发生封闭事务,TransactionManagers帮助我们隐藏问题(并且可以使用堆栈跟踪来记录它们)。

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

东西,你可能做的就是添加第二个参数去Delete()方法,确定是否使用交易与否和DeleteNode()false PARAM称之为:

//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; 
    } 
}