前面分别介绍了数据源架构模式之表数据入口、数据源架构模式之行和数据入口数据源架构模式之活动记录,相较于这三种数据源架构模式,数据映射器显得更加“高大上”。
一、概念
数据映射器(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对象在幕后运作,可以应用于各种对象关系映射。而与之带来的是需要创建大量具体的映射器类。不过现在框架都可以通过程序自动生成了。
四、使用时机
使用数据库映射器的主要是数据库方案和对象模型需要彼此独立演变的时候。最常见的当然是和领域模式一起使用。数据映射器无论是在设计阶段、开发阶段,还是测试阶段,在领域模型上操作时可以不考虑数据库。领域对象对数据库的结构一无所知,因为所有这些对应关系都由数据映射器完成。
当然,数据映射器引入了新的层次,因此使用这些模式的前提条件是业务逻辑的复杂性,如果很简单,那就没必要了。
如果没有领域模型,我不会选用数据映射器。但是没有数据映射器时,能使用领域模型吗?如果领域模型简单,且数据库受领域模型开发者的控制,则领域对象用活动记录直接访问数据库也是合理的。
不必创建完全意义上的数据库映射层。创建这样的数据映射器很复杂。大多数情况下,建议使用开源的数据库映射层而不是自己动手创建
相关推荐
数据源架构模式是设计应用程序时用于管理数据库交互的一种策略,它们旨在提高代码的可维护性和可重用性。表入口模式是这四种主要模式之一,它提供了一个对象来集中处理与特定数据库表相关的所有操作。 首先,我们来...
它提供了一种模型-视图-控制器的架构模式,帮助开发者分离业务逻辑与用户界面,简化了Web应用的开发。在多数据源环境下,SpringMVC可以通过DispatcherServlet和HandlerMapping等组件,根据不同的请求动态选择对应的...
它提供了一个模型-视图-控制器(MVC)架构模式,使得开发者可以将业务逻辑、数据处理和用户界面清晰地分离开来。Spring MVC通过DispatcherServlet处理HTTP请求,并将这些请求路由到适当的处理器。 **JPA(Java ...
在IT行业中,构建大型分布式系统时,数据源的动态切换是一项关键能力,它允许系统根据业务需求选择不同的数据库进行操作。本项目“Spring+SpringMvc+MybatisPlus+Aop(自定义注解)动态切换数据源”正是针对这一需求...
Spring Boot 多数据源项目是一种常见的企业级应用架构设计,它允许应用程序同时连接并操作多个数据库。在实际业务中,这种设计模式对于处理不同类型的业务数据或者实现数据隔离具有显著优势。Spring Boot作为现代化...
综合上述,这个项目是一个基于Maven构建的Web应用,集成了SpringMVC作为控制器,Mybatis作为数据访问层,实现了多数据源的支持,能够连接和操作不同的数据库。同时,它利用Jersey提供RESTful API,方便前后端交互。...
在实际应用中,这种多数据源架构能够提高系统的可扩展性和可靠性。通过读写分离,可以有效地分担数据库的压力,提升系统性能;通过分库分表,可以处理大规模数据,防止单一数据库成为系统瓶颈。同时,该架构也便于...
通过在MyBatis的配置文件中定义不同的数据源,我们可以为每个数据源创建独立的Mapper接口和XML映射文件。 在具体实现中,我们通常会创建两个或更多的数据源实例,分别对应不同的数据库。例如,一个主数据源用于写...
本文将深入探讨标题和描述中所提及的关键知识点,包括异构数据源转换的挑战、统一数据模型的设计、数据转换技术以及分布式转换架构设计等方面。 ### 一、数据异构性挑战分析 #### 1. 数据格式和结构差异 - **挑战...
**SpringMVC**:作为Spring框架的一部分,SpringMVC是一种模型-视图-控制器(MVC)架构模式,专门用于处理HTTP请求和响应。它提供了一种分离关注点的方式,使得开发者可以独立地设计业务逻辑、视图渲染和控制流程。...
三层架构是一种常见的软件设计模式,尤其在企业级应用开发中被广泛应用。这种架构将应用程序分为三个主要层次:表现层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access Layer...
【企业应用系统架构介绍】通常涉及到多个层面,包括操作管理、安全性、通信、用户界面组件、UI处理组件、数据访问组件、业务工作流、业务组件、用户、业务实体、服务代理、服务接口和数据源等。其中,MVC(Model-...
本项目以SpringMVC作为基础架构,利用CXF实现RESTful接口,通过Maven进行项目管理,并采用阿里数据源和MyBatis作为持久层解决方案,旨在构建一个功能齐全、易于维护的Web应用。下面将详细介绍这些技术及其在项目中的...
三层架构是一种常见的软件...同时,工厂模式使得在切换不同的数据源时,只需要更改工厂类的实现,而无需修改业务逻辑层和表示层的代码。这种设计模式的结合在大型项目中尤为常见,能有效提高软件的健壮性和可维护性。
业务逻辑处理来自Controller的请求,执行相关计算或验证,并可能涉及到多个数据源的操作。数据访问层则负责与数据库或其他数据存储进行交互,例如使用JDBC(Java Database Connectivity)进行SQL操作,或者使用ORM...
它提供了模型-视图-控制器(MVC)架构模式,使得开发者可以更轻松地处理HTTP请求和响应。通过使用注解,我们可以减少XML配置,使代码更加简洁易懂。 MyBatis是一个持久层框架,它允许开发者将SQL语句与Java代码紧密...
在C#中,这种架构模式尤其适用于大型企业级应用,因为它们通常需要处理复杂的业务规则和大量的数据操作。 首先,我们来看数据访问层(Data Access Layer,DAL)。这一层是直接与数据库进行交互的部分,负责执行SQL...
3. **数据持久层**:由Hibernate组成,负责从数据源中获取数据,生成持久化对象(Persistent Object),并将这些对象传递给业务逻辑层。在Hibernate中,持久化对象可以由值对象(Value Object)充当。 4. **数据源...
C#三层架构是一种常见的软件设计模式,用于组织和构建大型、可维护的.NET应用程序。它将应用程序分为三个主要层次:表现层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access ...