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存储引擎才有事务功能。