一、概念
identity Map:通过在映射中保存每个已经加载的对象,确保每个对象只加载一次,当要访问对象的时候,通过映射来查找它们。其实在数据源架构模式之数据映射器代码中有提及到标识映射,Mapper类的getFromMap方法就是实现标识映射的实现。
二、为什么要使用标识映射?
//client代码
$venue = new venue();
$venue->setName("XXXXXXX");
//插入一条数据
$mapper = new VenueMapper();
$mapper->insert($venue);
//获取刚插入的数据
$venueInfo = $mapper->find($venue->getId());
//修改数据
$venue->setName('OOOOOOOOOOO');
$mapper->update($venue);
$venue对象和$venueInfo对象是完全等同的,只是变量不同,在同一请求中,可能多次使用同一个对象。如果你修改了该对象的某个版本并保存到数据库中,如果确保该对象的另一个版本不会被你的修改所覆盖?不仅如此,重复的对象还可能导致系统性能降低。在一个进程中,一些常用的对象可能会被调用三四次,没必要每次都把对象重新保存到数据库中。标识映射可以很容易地解决这些问题。
三、实现标识映射
一个标识映射只是一个对象,它的任务就是跟踪系统中所有对象,并帮助系统避免将一个对象看成两个对象。
class ObjectWatcher {
private $all = array();
private static $instance;
private function __construct() { }
static function instance() {
if ( ! self::$instance ) {
self::$instance = new ObjectWatcher();
}
return self::$instance;
}
function globalKey( DomainObject $obj ) {
$key = get_class( $obj ).".".$obj->getId();
return $key;
}
static function add( DomainObject $obj ) {
$inst = self::instance();
$inst->all[$inst->globalKey( $obj )] = $obj;
}
static function exists( $classname, $id ) {
$inst = self::instance();
$key = "$classname.$id";
if ( isset( $inst->all[$key] ) ) {
return $inst->all[$key];
}
return null;
}
}
在数据源架构模式之数据映射器中利用此映射
private function getFromMap( $id ) {
return ObjectWatcher::exists( $this->targetClass(), $id );
}
说到这感觉是不是有点像注册表?
四、小结
如果在创建对象或添加对象到数据库时使用标识映射,系统中对象出现重复的可能性就变成了0.
当然,这只能对当前进程有效。不同进程间不可避免地会在同一时间访问同一个对象的不同版本。有时要考虑到并发访问可能会引起冲突导致数据损坏。如果问题严重,可能需要采用一定的“锁定”策略。也可以考虑将对象保存到共享内存中或者使用一个外部对象缓存系统,如memcached。