作者:老王
事务是极其重要的,不然代码就和炸弹差不多,即便现在它能正常运行,可是说不定啥时候就会爆炸。
先看看一般情况下,我们是如何实现事务处理的,以AdoDB为例,代码如下:
$conn->BeginTrans();
$ok = $conn->Execute($sql);
if ($ok) $ok = $conn->Execute($sql2);
if (!$ok) $conn->RollbackTrans();
else $conn->CommitTrans();
中规中矩的代码,虽然这样写没什么大毛病,但是早晚有一天,你会发现代码里到处充斥着和事务处理相关的代码,如果你不想到时候手忙脚乱的重构代码,就随我来看看如何实现透明化事务处理。
先来说说透明,它意味着我们的主体代码不应该显式的声明事务,取而代之的应该是隐式的配置事务。
再来看看事务的位置,对一个MVC程序来说,Controller中的Action是个绝好的位置,因为它勾画了事务的自然边界,在Action开始的时候打开事务,结束的时候提交事务,中间如果出现了问题就回滚事务。
现在我们要做的就是如何才能把事务处理透明的融合到Action中去。方法有很多,先唠叨一个很玄妙的方法:
class FooAction extends Action {
/**
* @transaction
*/
function bar() {
// do something
}
// something else
}
我们在方法的注释上标上预先定好的标志,比如@transaction,这样,当在FrontController中处理这个Action的时候,我们可以通过反射功能先捕捉方法是否声明了@transaction,如果声明了的话,则在FrontController里操作事务的打开,提交,回滚。但是我不喜欢这样的方式,因为它太过玄妙,通过反射解析注释,怎么看都觉得是奇技淫巧。
下面讲讲另一个方法,利用装饰模式实现透明化事务,因为我感冒了,不想多写了,直接贴代码:
######################################################################
abstract class Wrapper extends Action {
protected $chain;
public function __construct($chain) {
$this->chain = $chain;
}
public function getChain() {
return $this->chain;
}
public function setChain($chain) {
$this->chain = $chain;
}
}
abstract class Action {
protected $wrappers;
abstract public function execute();
public function getWrappers() {
return $this->wrappers;
}
public function setWrappers($wrappers) {
$this->wrappers = $wrappers;
}
}
######################################################################
class AuthorizationWrapper extends Wrapper {
public function execute() {
echo "Authorization begin<br />\r\n";
$this->getChain()->execute();
echo "Authorization end<br />\r\n";
}
}
class TransactionWrapper extends Wrapper {
public function execute() {
echo "Transaction begin<br />\r\n";
try {
$this->getChain()->execute();
} catch (Exception $e) {
// rollBack
}
// commit
echo "Transaction end<br />\r\n";
}
}
######################################################################
class FooAction extends Action {
protected $wrappers = array('authorization', 'transaction');
public function execute() {
echo "<br />\r\n";
echo "FooAction::execute()<br />\r\n";
echo "<br />\r\n";
}
}
######################################################################
$action = new FooAction();
$wrappers = $action->getWrappers();
for ($i = count($wrappers) - 1; $i >= 0; $i--) {
$class = ucfirst($wrappers[$i] . 'Wrapper');
$action = new $class($action);
}
$action->execute();
######################################################################
代码中,为了说明装饰模式的用法,我使用了两个装饰对象,一个Authorization,一个Transaction,你只要看准Transaction就可以了。
运行上面的代码,屏幕会出现如下结果:
Authorization begin
Transaction begin
FooAction::execute()
Transaction end
Authorization end
可以看到,Action动作已经被Transaction包裹起来了,如果Action在执行过程中,抛出了异常,那么Transaction会自动完成回滚,否则就会提交。事务的支持是通过在Action对象里制定wrappers属性实现的,也算是比较透明了。哈哈,我们的目标算是达到了。
还有一点需要注意的就是:不能在Action里直接跳转,否则Transaction没法提交或者回滚,为了避免这个问题,我们需要实现一个Response对象,用来封装响应,在Action结束后才决定是跳转还是渲染一个页面。
======================================
后记:PHP5.3就要到来了,我也琢磨着写一个新的PHP框架,上面说的透明事务处理这个特色肯定会是这个框架的一部分,预计还会有很多别的比较酷的特色。不过一个人的力量总是有限的,不知道最后这个框架能不能实现。如果你有兴趣,我们可以一起来做,欢迎留言拍砖。 |