`

数据源架构模式之数据映射器

阅读更多

前面分别介绍了数据源架构模式之表数据入口数据源架构模式之行和数据入口数据源架构模式之活动记录,相较于这三种数据源架构模式,数据映射器显得更加“高大上”。

 

一、概念

数据映射器(Data Mapper):在保持对象和数据库(以及映射器本身)彼此独立的情况下,在二者之间移动数据的一个映射器层。概念永远都是抽象的,简单的说,数据映射器就是一个负责将数据映射到对象的类数据。

 

二、为什么要使用数据映射器?

数据映射器实现起来比前三种模式都要复杂,那为什么还要使用它呢?

对象间的组织关系和关系数据库中的表是不同的。数据库表可以看成是由行与列组成的格子,表中的一行可以通过外键和另一个表(甚至同一个表)中的一行关联,而对象的组织关系更为复杂:一个对象可能包含其他对象;不同的数据结构可能通过不同的方式组织相同的对象。

 

对象和关系数据库之间的这种分歧被称为“对象关系阻抗不匹配”或“阻抗不匹配”。

数据映射器可以很好地解决这个问题,由它来负责对象和关系数据库两者数据的转换,从而有效地在领域模型中隐藏数据库操作并管理数据库转换中不可以避免的冲突。

 

 

三、简单实现数据映射器

<?php
//领域抽象类
abstract class DomainObject {
    private $id = -1;

    function __construct( $id=null ) {
        if ( is_null( $id ) ) {
            $this->markNew();
        } else {
            $this->id = $id;
        }
    }

    function getId( ) {
        return $this->id;
    }

    static function getCollection( $type ) {
        //这里通过一个工广生成此对象对应的数组数据对象
        return HelperFactory::getCollection( $type ); 
    }
 
    function collection() {
        return self::getCollection( get_class( $this ) );
    }

    function finder() {
        return self::getFinder( get_class( $this ) );
    }

    static function getFinder( $type ) {
        //这里通过一个工厂生成此对象对应的map对象
        return HelperFactory::getFinder( $type ); 
    }

    function setId( $id ) {
        $this->id = $id;
    }

    function __clone() {
        $this->id = -1;
    }
}

//场所类
class Venue extends DomainObject {
    private $name;
    private $spaces;

    function __construct( $id=null, $name=null ) {
        $this->name = $name;
        parent::__construct( $id );
    }
    
    function setSpaces( SpaceCollection $spaces ) {
        $this->spaces = $spaces;
    } 

    function getSpaces() {
        if ( ! isset( $this->spaces ) ) {
            //创建对应的SpaceMapper对象
            $finder = self::getFinder( 'Space' ); 
            $this->spaces = $finder->findByVenue( $this->getId() );
            //$this->spaces = self::getCollection("Space");
        }
        return $this->spaces;
    } 

    function addSpace( Space $space ) {
        $this->getSpaces()->add( $space );
        $space->setVenue( $this );
    }

    function setName( $name_s ) {
        $this->name = $name_s;
    }

    function getName( ) {
        return $this->name;
    }
    
    static function findAll() {
        $finder = self::getFinder( __CLASS__ ); 
        return $finder->findAll();
    }
    static function find( $id ) {
        $finder = self::getFinder( __CLASS__ ); 
        return $finder->find( $id );
    }

}


abstract class Mapper{
    protected static $PDO; 
    function __construct() {
 
        if ( ! isset(self::$PDO) ) { 
            //此处可加缓存
            self::$PDO = new PDO( $dsn );
            self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
    }

    private function getFromMap( $id ) {
        //从内存取出此$id的DomainObject对象
    }

    private function addToMap( DomainObject $obj ) {
        //将此DomainObject对象加入到内存
    }

    function find( $id ) {
        $old = $this->getFromMap( $id );
        if ( $old ) { return $old; }
        $this->selectstmt()->execute( array( $id ) );
        $array = $this->selectstmt()->fetch( ); 
        $this->selectstmt()->closeCursor( );
        if ( ! is_array( $array ) ) { return null; }
        if ( ! isset( $array['id'] ) ) { return null; }
        $object = $this->createObject( $array );
        return $object; 
    }

    function findAll( ) {
        $this->selectAllStmt()->execute( array() );
        return $this->getCollection( $this->selectAllStmt()->fetchAll( PDO::FETCH_ASSOC ) );
    }
 
    function createObject( $array ) {
        $old = $this->getFromMap( $array['id']);
        if ( $old ) { return $old; }
        $obj = $this->doCreateObject( $array );
        $this->addToMap( $obj );
        return $obj;
    }

    function insert( DomainObject $obj ) {
        $this->doInsert( $obj ); 
        $this->addToMap( $obj );
    }

    protected abstract function getCollection( array $raw );
    protected abstract function doCreateObject( array $array );
    protected abstract function doInsert( DomainObject $object );
    protected abstract function targetClass();
    protected abstract function selectStmt( );
    protected abstract function selectAllStmt( );
}

class VenueMapper extends Mapper {

    function __construct() {
        parent::__construct();
        $this->selectAllStmt = self::$PDO->prepare( 
                            "SELECT * FROM venue");
        $this->selectStmt = self::$PDO->prepare( 
                            "SELECT * FROM venue WHERE id=?");
        $this->updateStmt = self::$PDO->prepare( 
                            "UPDATE venue SET name=?, id=? WHERE id=?");
        $this->insertStmt = self::$PDO->prepare( 
                            "INSERT into venue ( name ) 
                             values( ? )");
    } 
    
    function getCollection( array $raw ) {
        //这里简单起见用个对象数组
        $ret = array();
        foreach ($raw as $value) {
            $ret[] = $this->createObject($value);
        }
        return $ret;
    }

    protected function doCreateObject( array $array ) {
        $obj = new Venue( $array['id'] );
        $obj->setname( $array['name'] );
        //$space_mapper = new SpaceMapper();
        //$space_collection = $space_mapper->findByVenue( $array['id'] );
        //$obj->setSpaces( $space_collection );
        return $obj;
    }

    protected function targetClass() {
        return "Venue";
    }

    protected function doInsert( DomainObject $object ) {
        $values = array( $object->getname() ); 
        $this->insertStmt->execute( $values );
        $id = self::$PDO->lastInsertId();
        $object->setId( $id );
    }
    
    function update( DomainObject $object ) {
        $values = array( $object->getname(), $object->getid(), $object->getId() ); 
        $this->updateStmt->execute( $values );
    }

    function selectStmt() {
        return $this->selectStmt;
    }

    function selectAllStmt() {
        return $this->selectAllStmt;
    }

}

//client代码

$venue = new venue();
$venue->setName("XXXXXXX");
//插入一条数据
$mapper = new VenueMapper();
$mapper->insert($venue);
//获取刚插入的数据
$venueInfo = $mapper->find($venue->getId());
//修改数据 
$venue->setName('OOOOOOOOOOO');
$mapper->update($venue);
?>

 代码省略了一些辅助类,保留最主要的领域对象和数据映射器。数据映射器模式最强大的地方在于消除了领域层和数据库操作之间的耦合。Mapper对象在幕后运作,可以应用于各种对象关系映射。而与之带来的是需要创建大量具体的映射器类。不过现在框架都可以通过程序自动生成了。

 

 

四、使用时机

使用数据库映射器的主要是数据库方案和对象模型需要彼此独立演变的时候。最常见的当然是和领域模式一起使用。数据映射器无论是在设计阶段、开发阶段,还是测试阶段,在领域模型上操作时可以不考虑数据库。领域对象对数据库的结构一无所知,因为所有这些对应关系都由数据映射器完成。

当然,数据映射器引入了新的层次,因此使用这些模式的前提条件是业务逻辑的复杂性,如果很简单,那就没必要了。

如果没有领域模型,我不会选用数据映射器。但是没有数据映射器时,能使用领域模型吗?如果领域模型简单,且数据库受领域模型开发者的控制,则领域对象用活动记录直接访问数据库也是合理的。

 

不必创建完全意义上的数据库映射层。创建这样的数据映射器很复杂。大多数情况下,建议使用开源的数据库映射层而不是自己动手创建

 

2
0
分享到:
评论

相关推荐

    大数据整理——数据集成.pdf

    数据库:提供数据,独⽴性强 封装器: 负责把上层⽤户的查询转发到数据源,并把数据源返回的结果转发给上层的应⽤ 中间模式:向下协调各数据源系统,向上为访问集成数据的应⽤提供统⼀数据模式和数据访问的通⽤接⼝...

    领域驱动设计与模式实战

    8.3.3 元数据映射(对象关系(O/R)映射工具) 8.3.4 再次选择 8.4 分类 8.4.1 领域模型风格 8.4.2 映射工具风格 8.4.3 起点 8.4.4 API焦点 8.4.5 查询风格 8.4.6 高级数据库支持 8.4.7 其他功能 8.5 另一个分类:...

    Java微服务架构163课

    015修改数据源 016常用 APT 命令 017Linux 用户和组管理 018查看目录和文件的权限 019更改操作权限 020安装 Java 021安装 Tomcat 022安装 MySQL 023部署项目 第3章 Docker实战开发 042设置镜像标签 ...

    Java微服务架构l零从基础到精通高清视频教程全套 163课

    015修改数据源 016常用 APT 命令 017Linux 用户和组管理 018查看目录和文件的权限 019更改操作权限 020安装 Java 021安装 Tomcat 022安装 MySQL 023部署项目 第3章 Docker实战开发 042设置镜像标签 024Docker ...

    greldal:在关系数据存储和GraphQL API之间进行双向映射的微框架(由Node.js提供支持)

    但是,如果您的主要数据源是关系数据库,那么将GraphQL查询映射到有效的数据库查询可能会很艰巨。凭借幼稚的解析器分层解析能力,很容易导致效率低下的数据访问模式和。缓存策略,数据加载器等可以部分缓解问题,但...

    validator:一个小助手,使您的数据符合数据模式

    导出结果输出文件有2个输出文件: 重组数据具有源和目标字段名称的2列数据映射文件资料格式输入数据格式可以是CSV,Geopackage(GPKG)或ESRI Shapefile(SHP)。细节随机播放在“探索”框中,仅显示10个唯一值,但...

    graphkit:基于JSON模式处理数据

    json:map :将JSON模式映射应用于来自源的数据。 rdf:load :将数据从JSON流导入三元组存储。 rdf:dedupe :基于某些外部映射文件应用sameAs映射。 rdf:sparql :运行SPARQL查询。 mql:query :运行一个MQL查询...

    asp.net知识库

    ASP.NET 2.0 中的数据源控件 使用 ASP.NET 2.0 ObjectDataSource 控件 ASP.NET 2.0 的内部变化 使用SQL Cache Dependency 代替 Ibatisnet 提供的CacheModel ASP.NET 2.0中小心Profile命名冲突 使用ASP.NET 2.0 ...

    ASP基于BS架构个人网站毕业设计(源代码+thesis).zip

    ASP.NET MVC是一种成熟的开发框架,它采用模型-视图-控制器的设计模式,将应用程序的逻辑和界面分离,提供了更好的代码组织和可维护性。我们选择ASP.NET MVC框架是因为它具有良好的可扩展性和灵活性,可以轻松地添加...

    elasticsearch-virtual-schema

    使用虚拟架构以只读模式从Exasol数据库访问ElasticSearch数据源。 目录 用户须知 在“找到所有文档。 给开发人员的信息 运行时依赖 运行虚拟模式需要Java Runtime版本11或更高版本。 相依性 目的 执照 虚拟架构...

    dsPIC33CK64MP105系列中文版数据手册(带书签).pdf

    • 高效代码型(C和汇编)架构 • 40位宽累加器 • 带双数据取操作的单周期(MAC/MPY) • 单周期混合符号乘法: - 32位乘法支持 • 快速的6周期除法 • 零开销循环 高速PWM • 4对PWM • 最佳PWM分辨率为250 ps • ...

    dsPIC33CK64MP105系列中文数据手册.pdf

    • 高效代码型(C和汇编)架构 • 40位宽累加器 • 带双数据取操作的单周期(MAC/MPY) • 单周期混合符号乘法: - 32位乘法支持 • 快速的6周期除法 • 零开销循环 高速PWM • 4对PWM • 最佳PWM分辨率为250 ps • ...

    基于SSM架构实现的大型分布式购物网站-B2C项目源码+项目说明.zip

    - 1、配置数据源 - 2、让spring容器管理SqlSessionFactory,单例存在 - 3、把mapper的代理对象放到spring容器中。使用扫描包的方式加载mapper的代理对象。 ###Service层 - 1、事务管理 - 2、需要把service实现类放...

    大数据的基础知识.pdf

    ⼤数据的基础知识 1、⼤数据的基础知识 ⼤数据的概念 ⼤数据(big data),IT⾏业术语,是指⽆法在⼀定时间范围内⽤常规软件⼯具进⾏捕捉、管理和处理的数据集合,是需要新处理模式才能 具有更强的决策⼒、洞察发现...

    JAVA上百实例源码以及开源项目源代码

     Java绘制图片火焰效果,源代码相关注释:前景和背景Image对象、Applet和绘制火焰的效果的Image对象、Applet和绘制火焰的效果的Graphics对象、火焰效果的线程、Applet的高度,图片到图片装载器、绘制火焰效果的X坐标...

    Android代码生成器

    真正面向对象设计:系统的整体设计,提供通过使用面向对象的方法,设计所需系统中的基础对象(类),并根据专业级的“三层架构模板”生成专业级的界面和源代码,同时设计系统和数据库:采用“数据映射”建立基础对象...

    克朗凯特:一位记者的**

    这些Web组件包括: 事件发射器(数据源) 活动消费者(听众) 要呈现的网页标签,包括风格布局事件有效负载转换有效负载转换和标记属性/属性之间的映射演示组件1.添加HTML标记/元素/ Web组件最简单的报告是具有单个...

Global site tag (gtag.js) - Google Analytics