Zend_Db_Adapter :: beginTransaction()堆栈吗?

时间:2011-02-08 05:35:37

标签: php zend-framework

请考虑以下事项:

/** (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内的begin transaction和commit / rollback调用。

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

2 个答案:

答案 0 :(得分: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()方法添加第二个参数,该方法决定是否使用交易,并使用false中的DeleteNode()参数调用它:

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

答案 1 :(得分:1)

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

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

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

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