Mangeto中执行某个功能时,如update、insert、delete,magento的model会自动对事务进行处理。这些写操作都集中到 model的save方法或者delete方法来处理,执行时候会调用resource model的beginTransaction方法来开启事务,调用resource model的commit方法来提交事务,调用resource model的rollBack方法来回滚事务。save方法代码如下:


        public function save()
         {
         /**
         * Direct deleted items to delete method
         */
         if ($this->isDeleted()) {
         return $this->delete();
         }
         if (!$this->_hasModelChanged()) {
         return $this;
         }
         $this->_getResource()->beginTransaction();//开启事务
         $dataCommited = false;
         try {
         $this->_beforeSave();
         if ($this->_dataSaveAllowed) {
         $this->_getResource()->save($this);
         $this->_afterSave();
         }
         $this->_getResource()->addCommitCallback(array($this, 'afterCommitCallback'))
         ->commit();//添加提交后的回调函数
         $this->_hasDataChanges = false;
         $dataCommited = true;
         } catch (Exception $e) {
         $this->_getResource()->rollBack();//异常时事务回滚
         $this->_hasDataChanges = true;
         throw $e;
         }
         if ($dataCommited) {
         $this->_afterSaveCommit();
         }
         return $this;
         }

delete方法和save类似,这里就不贴代码出来分析了。

多个model的事务处理

单个model的事务处理很简单,magento的model层直接就处理了,不需要额外的代码。那么当我们的业务同时涉及到多个model,并且必须是当成一个事务来处理的时候,该怎么处理呢?不用担心,像magento这么优秀的系统不可能不会考虑到这些情况的。于是类 Mage_Core_Model_Resource_Transaction派上用场了。一个典型的多model事务处理如下:

        
$transaction = Mage::getModel('core/resource_transaction');
        $transaction->addObject($model1);
        $transaction->addObject($model2);
        $transaction->addObject($model3);
        //...
         
        try {
         $transaction->save();
        //或者进行删除操作$transaction->delete();
         } catch (Exception $e) {
        //异常处理
        }

那么当我们通过addObject把相关的model都添加到事务处理器 Mage_Core_Model_Resource_Transaction里面并执行save或者delete的时候,做了什么呢?我们以save为例子进行深入。

        
public function save()
         {
         $this->_startTransaction();//开启事务
         $error = false;
         
        try {
         foreach ($this->_objects as $object) {
         $object->save();//遍历添加进来的oject并执行其save方法
         }
         } catch (Exception $e) {
         $error = $e;
         }
         
        if ($error === false) {
         try {
         $this->_runCallbacks();//执行回调函数
         } catch (Exception $e) {
         $error = $e;
         }
         }
         
        if ($error) {
         $this->_rollbackTransaction();//异常,回滚事务
         throw $error;
         } else {
         $this->_commitTransaction();
         }       
        return $this;
         }
         
        protected function _startTransaction()
         {
        //遍历相关对象执行对象的resource model的beginTransaction方法开启事务
         foreach ($this->_objects as $object) {
         $object->getResource()->beginTransaction();
         }
         return $this;
         }
         
        protected function _commitTransaction()       
        {
        //遍历相关对象执行对象的resource model的commit方法提交事务
         foreach ($this->_objects as $object) {
         $object->getResource()->commit();
         }
         return $this;
         }
         
        protected function _rollbackTransaction()
         {
        //遍历相关对象执行对象的resource model的rollBack方法提交事务
         foreach ($this->_objects as $object) {
         $object->getResource()->rollBack();
         }
         return $this;
         }

代码很容易理解和单个model的save方法流程是一致的,只是开启事务、提交事务或者回滚事务的时候是遍历执行各个model的相关方法。

那么这个时候我们就会有这样的疑惑:开启事务的方法执行了好几遍,这样会不会出问题呢?如果是真的多次执行数据库对事务相关的sql的话,那么肯定是会有问题的。 magento很好的处理了这个问题。就是resouce model层对事务有了一个层级的概念。多次提交事务,只有第一次才是真的提交事务,后面的多次只是事务的level计数器加一,同样的对于commit 和rollback只有计数器为0的时候才真正执行相关的操作,其他时候都是计数器减一,这样就避免了我们前面提出的那个问题的出现了。看相关代码:


        //file:lib/Varien/Db/Adapter/Pdo/Mysql.php
        public function beginTransaction()
         {
         if ($this->_transactionLevel === 0) {
         $this->_debugTimer();
         parent::beginTransaction();
         $this->_debugStat(self::DEBUG_TRANSACTION, 'BEGIN');
         }
         ++$this->_transactionLevel;
         return $this;
         }
         
        public function commit()
         {
         if ($this->_transactionLevel === 1) {
         $this->_debugTimer();
         parent::commit();
         $this->_debugStat(self::DEBUG_TRANSACTION, 'COMMIT');
         }
         --$this->_transactionLevel;
         return $this;
        }
         
        public function rollback()
         {
         if ($this->_transactionLevel === 1) {
         $this->_debugTimer();
         parent::rollback();
         $this->_debugStat(self::DEBUG_TRANSACTION, 'ROLLBACK');
         }
         --$this->_transactionLevel;
         return $this;
         }

最后提醒一点,对于mysql来说,只有innodb存储引擎才有事务功能。

360magento提供专业的基于magento系统的电商网站开发服务,如有需求或相关咨询,请与我们联系