数据映射器通常是啥样的?

Posted

技术标签:

【中文标题】数据映射器通常是啥样的?【英文标题】:What does a Data Mapper typically look like?数据映射器通常是什么样的? 【发布时间】:2009-12-28 17:46:13 【问题描述】:

我有一个名为Cat 的表和一个名为Catphp 类。现在我想做一个CatDataMapper 类,这样Cat extends CatDataMapper

我希望 Data Mapper 类提供执行 ORM 以及创建、编辑和删除 Cat 的基本功能。

为此,也许非常了解这种模式的人可以给我一些有用的建议?我觉得只提供一些函数比如update()、delete()、save()有点太简单了。

我意识到 Data Mapper 有这个问题:首先创建 Cat 的实例,然后初始化所有变量,如 name、furColor、eyeColor、purrSound、meowSound、attendants 等。一切都设置好后,你调用从 CatDataMapper 继承的 save() 函数。这很简单;) 但现在,真正的问题是:您查询数据库中的猫并返回一个包含大量猫数据的简单无聊的结果集。

PDO 具有一些创建 Cat 实例的 ORM 功能。可以说我使用它,或者甚至可以说我有一个带有关联数组的 mapDataset() 函数。然而,一旦我从数据集中获得了我的 Cat 对象,我就有了冗余数据。同时,20 个用户可以从数据库中获取相同的猫数据并编辑猫对象,即重命名猫,然后 save() 它,而另一个用户仍然在设置另一个 furColor。当他们都保存编辑时,一切都搞砸了。

呃...好吧,让这个问题保持简短:这里有什么好的做法?

【问题讨论】:

【参考方案1】:

来自DataMapper in PoEA

数据映射器是软件层 分离内存中的对象 从数据库。它的责任 是在两者之间传输数据 并将它们与每个 其他。使用内存中的 Data Mapper 对象甚至不需要知道有 存在数据库;他们不需要 SQL 接口代码,当然没有 数据库模式的知识。 (这 数据库模式总是不知道 使用它的对象。)因为它是 映射器 (473) 的形式,数据映射器 本身甚至对域来说都是未知的 层。

因此,Cat 不应扩展 CatDataMapper,因为这会创建 is-a 关系并将 Cat 绑定到 Persistence 层。如果您希望能够以这种方式处理 Cats 的持久性,请查看 ActiveRecord 或任何其他数据源架构模式。

在使用领域模型时,您通常会使用 DataMapper。一个简单的 DataMapper 只会在字段到字段的基础上将数据库表映射到等效的内存类。但是,当需要 DataMapper 时,通常不会有这么简单的关系。表格不会 1:1 映射到您的对象。相反,多个表可以形成一个对象聚合,反之亦然。因此,实现只是 CRUD 方法很容易成为一个相当大的挑战。

除此之外,它是更复杂的模式之一(在 PoEA 中涵盖 15 页),通常与Repository pattern 等结合使用。查看此页面右侧的相关问题列中是否有类似问题。

关于您关于多个用户编辑同一个 Cat 的问题,这是一个常见的问题,称为 Concurrency。一种解决方案是locking the row,而有人编辑它。但是像所有东西一样,这可以lead to other issues。

【讨论】:

我有一个关于数据映射器与表数据网关 (TDG) 的问题。 TDG模式表与表1:1映射,但在一些TDG类方法中,我们经常需要将主表与其他表连接起来(与一些父表连接以选择附加信息)。这样的事情是否违反了表数据网关模式?恕我直言,只要您在 TDG 类方法中的 SQL 语句中添加其他数据库表(甚至连接),或者将您的模型(域模型或表数据)类拆分为两个 - 该类就成为数据映射器类。恕我直言,数据映射器只是 TDG 类的正常演变。 另外,如果您实现的数据源类能够连接到各种源(数据库或某些 Web 服务),那么恕我直言,您已经实现了数据映射器类。 Fowler 在 POEAA 中指出,TDG 可能包含访问许多表/视图的代码,所以我猜您也可以在其中执行 JOIN。但当然,这意味着返回的结果将不再与表 1:1 匹配,并且除了通用行之外,您还需要对这些行进行专门的更新和插入查询。我可以看到这如何使您得出结论,它看起来像 Data Mapper,但 Data Mapper 的目的实际上远比这复杂得多。这不仅仅是 JOIN => DataMapper 。看看css.dzone.com/books/practical-php-patterns/… 是的,我同意 DataMapper 适用于更复杂的任务。不同的数据源呢?如果我实现的 TDG 类能够连接到各种源(数据库或某些 Web 服务,取决于传递给构造函数的参数),那么恕我直言,我的类不再是 TDG 类,而是更多的 Data Mapper 类?我假设有时可能很难在理论上严格:是实现表网关或数据映射器模式的数据源类。 我认为在这种情况下它更像Remote Facade。但为什么不直接买书呢?反正就是经典。【参考方案2】:

如果你依赖像Doctrine 或Propel 这样的ORM,基本原理是创建一个静态类,从数据库中获取实际数据(例如Propel 将创建CatPeer),并通过以下方式检索结果然后 Peer 类将被“水合”成 Cat 对象。

水合过程是将“普通无聊”的 mysql 结果集转换为具有 getter 和 setter 的好对象的过程。

因此,对于检索,您可以使用 CatPeer::doSelect() 之类的内容。然后对于一个新对象,您首先要实例化它(或从数据库中检索和实例化): $cat = new Cat();

插入就像这样做一样简单:$cat->save(); 这相当于插入(或者如果对象已经存在于数据库中,则更新...... ORM 应该知道如何区分 new 和现有对象,例如,使用主键的存在或不存在)。

【讨论】:

【参考方案3】:

在 PHP

    使用某种解决方法,例如序列化对象,修改其字符串表示形式,然后使用反序列化将其恢复 公开所有字段 保持它们私有/受保护,并为它们中的每一个编写修改器/访问器

第一种方法可能会在新版本中中断,并且是非常粗暴的 hack,第二种方法被认为是(非常)糟糕的做法。

第三个选项也被认为是不好的做法,因为您不应该为所有字段提供 getter/setter,只为需要它的字段提供。从纯 DDD(领域驱动设计)的角度来看,您的模型会“损坏”,因为它包含仅因持久性机制而需要的方法。 这也意味着现在您必须在字段 -> 表列旁边为字段 -> 设置方法描述另一个映射。

PHP 5.3 引入了通过使用反射访问/更改所有类型字段的能力:

http://hu2.php.net/manual/en/reflectionproperty.setaccessible.php

这样,您就可以实现真正的数据映射器,因为不再需要为所有字段提供 mutator。

【讨论】:

【参考方案4】:

PDO 具有一些 ORM 功能 创建 Cat 实例。可以说我使用 那个,或者甚至可以说我有一个 mapDataset() 函数接受一个 关联数组。然而,一旦 我从数据集中得到了我的 Cat 对象,我 有冗余数据。同时, 二十个用户可以选择相同的 cat 数据库中的数据并编辑 猫对象,即重命名猫, 并保存()它,而另一个用户 仍然是关于设置另一个的事情 毛皮颜色。当所有人都拯救他们的 编辑,一切都搞砸了。

为了跟踪数据的状态,通常会使用 IdentityMap 和/或 UnitOfWork 来跟踪映射实体上的所有不同操作...然后在请求周期结束时,这些操作将是执行。

【讨论】:

【参考方案5】:

保持简短的回答: 你有一个 Cat 的实例。 (也许它扩展了 CatDbMapper 或 Cat3rdpartycatstoreMapper) 你打电话:

$cats = $cat_model->getBlueEyedCats();
//then you get an array of Cat objects, in the $cats array

不知道你用什么,你可以看看一些php框架来更好地理解。

【讨论】:

我认为您描述的是 ActiveRecord,而不是 DataMapper。

以上是关于数据映射器通常是啥样的?的主要内容,如果未能解决你的问题,请参考以下文章

映射器何时将其输出存储到其本地硬盘?

根据映射器代码中的某些逻辑,将映射器中的一些数据(行)写入单独的目录

MyBatis映射器

如果映射器在中途失败并且 Hadoop 重试该映射器,自定义计数器会发生啥

双层设备映射器 - 自定义 dm-crypt

keycloak 客户端协议映射器(脚本映射器)将请求标头添加到令牌中