对象关系行为模式之工作单元
一、概念
Unit of Work:维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决。其UML结构大致如下:
工作单元记录在业务事务过程中对数据库有影响的所有变化。操作结束后,作为一种结果,工作单元了解所有需要对数据库做的改变,统一对数据库操作。
二、为什么要使用工作单元?
如果没有使用工作单元,可以在每次修改对象模型时对数据库对得相应修改,但这样会产生大量规模很小的数据库调用,从而导致速度变慢。而且这样做还需要有一个对整个交互过程都开放的事务,如果存在一个贯穿多个请求的业务事务,这就是不合实际的。如果还要记录读过的对象以避免不致读,那么情况会更糟。
引发处理数据库的原因就是变化:创建新对象和更新或删除已经存在的对象。工作单元就是一个记录这些变化的对象。只要开始做一些可能会对数据库有影响的操作,就创建一个工作单元去记录这些变化。每当创建、改变或者删除一个对象时,就通知此工作单元。也可以让此单元知道所读过的对象,通过验证在整个业务事务处理过程中数据库中的所有对象都没有改变,从而检查一致读。
三、实现工作单元
在对象行为关系模式之标识映射代码中加入
static function addDelete( DomainObject $obj ) { $self = self::instance(); $self->delete[$self->globalKey( $obj )] = $obj; } static function addDirty( DomainObject $obj ) { $inst = self::instance(); if ( ! in_array( $obj, $inst->new, true ) ) { $inst->dirty[$inst->globalKey( $obj )] = $obj; } } static function addNew( DomainObject $obj ) { $inst = self::instance(); // we don't yet have an id $inst->new[] = $obj; } static function addClean(DomainObject $obj ) { $self = self::instance(); unset( $self->delete[$self->globalKey( $obj )] ); unset( $self->dirty[$self->globalKey( $obj )] ); $self->new = array_filter( $self->new, function( $a ) use ( $obj ) { return !( $a === $obj ); } ); } function performOperations() { foreach ( $this->dirty as $key=>$obj ) { $obj->finder()->update( $obj ); } foreach ( $this->new as $key=>$obj ) { $obj->finder()->insert( $obj ); } $this->dirty = array(); $this->new = array(); }
ObjectWatcher类仍然是一个标识映射,但增加了跟踪系统中所有对象的功能(通过$all)。
通过addDirty方法,将“脏”(就是被修改了)对象保存在$dirty数组中,直到更新数据库。
通过addClean方法,把“脏”对象标识为“干净”的,这样数据库就不会更新。
通过addNew方法,将新创建的对象添加到$new数组中,该数组中的对象将会被插入到数据库中。
通过performOperations方法,遍历$dirty和$new数组,更新或添加对象。
四、小结
工作单元解决的基本问题是记录操作过的各种对象,以便知道为了使内存中的数据与数据库同步需要考虑哪些对象。如果能够在一个系统事务中做完所有的工作,则只需要考虑那些改变了的对象。尽管一般来说,工作单元是解决这个问题的最好途径,但还有其他方法。
也许最简单的方法是,在修改任何一个对象时就显式地保存该对象,但是带来的问题是可能使用的数据库调用比预想的多。如果操作过程中在三个不同的地方改变了同一个对象,最终会产出三次数据库调用,而不是一次。
要避免多重数据库调用,可以把对数据库的更新操作放在最后。为了做到这一点,需要记录已经改变的所有对象。可以在代码中用变量来实现记录跟踪,但是一旦变量很多,变量很快就变得难以管理。一般来说,变量与事务脚本运行得很好,但很难与领域模型一起使用。
把每个所改变的对象加上“脏”标志的做法要比把对象保存在变量中好。在事务处理完后,需要找出所有加了“脏”标记的对象并把它们写入到数据库中。这项技术的价值取决于寻找“脏”对象的难易程序。如果所有的“脏”对象都在一个单一层次结构上,就可以遍历该层结构来向数据库写入所有被改变了的对象。然而,跨越一个更一般的对象网络(如一个领域模型)是比较困难的。
工作单元的强大功能是把所有的信息保存在一个地方。一旦使用了工作单元,就不必为记录所做的修改做很多操作。而且,工作单元还可以作为更复杂情况下的固定处理平台。