数据映射器和关系:实施策略?

ope*_*rog 21 oop design-patterns datamapper

我差不多完成了我的数据映射器,但现在我正处于关系的关键时刻.

我会试着在这里说明我的想法.我无法找到关于这个主题的好文章/信息,所以也许我正在重新发明轮子(我确定,我可以使用一个大框架 - 但我想通过这样做来学习).

1:1关系

首先,让我们看一下1:1的关系.通常,当我们有一个名为"Company"的域类和一个名为"Address"的域类时,我们的Company类将具有类似address_id的内容.让我们说在大多数情况下我们只显示公司列表,只有当有人查看详细信息时才需要地址.在这种情况下,我的数据映射器(CompanyDataMapper)只是懒惰加载,这意味着它只从数据库中获取该address_id,但也不会进行连接以获取地址数据.

一般来说,我对每个关系都有一个getter方法.所以在这种情况下,有一个getAddress(Company companyObject)方法.它需要一个公司对象,查找它的地址属性,如果它是NULL,则使用该Address对象的Mapper类(AddressDataMapper)从数据库中提取相应的Address对象,并将该地址对象分配给指定的address属性.公司对象.

重要提示:数据映射器是否允许使用其他数据映射器?

让我们说在大多数情况下,您需要公司对象和地址对象,因为您始终将它一起显示在列表中.在这种情况下,CompanyDataMapper不仅可以获取公司对象,还可以使用JOIN进行SQL查询,以获取地址对象的所有字段.最后,它迭代记录集并使用相应的值提供新对象,将地址对象分配给公司对象.

听起来很简单,到目前为止.

1:n关系

这些怎么样?与1:1的唯一区别在于公司可能有多个Address对象.让我们来看看:当我们大部分时间只对公司感兴趣时,Data Mapper只会将公司对象的addresses属性设置为NULL.addresses属性是一个可以引用无,一个或多个地址的数组.但是我们还不知道,因为我们懒得加载,所以它只是NULL.但是,如果在大多数情况下我们还需要所有地址呢?如果我们要显示包含所有公司及其所有地址的大清单?在这种情况下,事情开始变得非常丑陋.首先,我们不能为每个地址对象加入地址表五十次(我坚信这是不可能的,如果是,性能将低于零).所以,当我们进一步思考这个问题时,在这种情况下不可能懒得加载.

重要提示:这是真的吗?如果我有10个公司,每10个地址,我必须发出100个查询才能获得100个地址对象吗?

m:n关系

让我们说一个地址对象只包含国家,州,城市,道路和门牌号码.但是,一栋房子可能是一座大型商业大厦,其中有很多公司.就像其中一个现代化的办公楼,任何人都可以在其网站上租一个小rom来炫耀那座塔楼.所以:许多公司可以共享相同的地址.

我还没有计划处理这类问题.

重要提示:可能与1:n关系相比,这不是一个更大的问题吗?

如果有人知道有关解决/实现这个问题的详细资源,我会很高兴找到一个链接!

Gui*_*nco 17

在我开始之前,我假设你从头到尾都读过福勒的PoEAA书.=)另外,我会考虑你已经考虑过在处理ORM时遇到的第一个初始问题.我可以突出显示一个简单的方法,例如使用相同的标识符多次调用DataMapper并始终返回相同的对象(读作IdentityMap).

重要提示:数据映射器是否允许使用其他数据映射器?

如果第二个是第二个弱引用,则只能让一个DataMapper访问另一个DataMapper.

让我们说在大多数情况下,您需要公司对象和地址对象,因为您始终将它一起显示在列表中.在这种情况下,CompanyDataMapper不仅可以获取公司对象,还可以使用JOIN进行SQL查询,以获取地址对象的所有字段.最后,它迭代记录集并使用相应的值提供新对象,将地址对象分配给公司对象.

你在这里讨论的问题在实践中听起来很简单,但它在幕后有点复杂.

首先,您不应该拥有getAddress(Company),而应该拥有Proxy对象.代理是给定实例的非初始化表示.在这种情况下,代理服务器包含您要查找的条目的引用.它必须从原始对象扩展,并需要提供初始化方法,以及相关的DataMapper来加载它.

关于一次加入和加载多个对象的第二部分称为Hydrator.水合器接收线条和列的扁平结构并转换为对象图形.但它确实进入了一个单独的问题:如果你纯粹处理对象,为什么要提取表?尝试采用对象提取方法将导致您实现一种OQL(对象查询语言).

重要提示:这是真的吗?如果我有10个公司,每10个地址,我必须发出100个查询才能获得100个地址对象吗?

处理一组对象是PHP的噩梦.是的,由于缺乏强大的集合实现,该语言很糟糕.基本上,您需要在这里处理不同的情况: - 新实例和此元素列表中的所有元素都是新的 - 新实例和此元素列表中的所有元素都是预先存在的 - 此实例和元素列表中的元素在新的和预先存在的实例之间混合 - 并且不触及元素列表上的任何内容 - 预先存在的实例和操作列表上的项目

我在这里非常简单,但我要强调的主要观点是需要一个Collection对象.其中有两个:一个处理新列表,另一个处理现有列表.处理现有列表的那个需要能够在您尝试访问其中的任何内容时加载该集合.这是没有n + 1问题的唯一方法.

在这里,它还强调了您必须处理的下一个大问题.关联可以是单向的或双向的.这意味着公司知道地址但地址不知道公司是单向的,而用户是许多组的一部分,组包含许多用户是双向关联.事情很容易成为一个噩梦,这就是为什么你需要映射模式来正确理解正在发生的事情.

处理多对多与处理集合一般是一样的.

你还没有考虑过一个重要的部分.如果我构建我的整个对象图(公司和地址)并且我决定坚持它们......它需要保持两者或者我是否必须手动告诉我想要保留什么?两种方式都有不同的问题.我们假设你想要第一种方法.您刚刚进入了我认为最复杂的设计模式之一:UnitOfWork.然后你必须处理排序要应用的实体的顺序而不产生约束问题(阅读关于如何解决这个问题的拓扑排序).如果你采用第二种方法,你可能很容易进入一种感觉你的工具被破坏的情况,主要是因为你的对象图很容易处于不一致的状态.

最后......你打算对继承做任何支持吗?如果是积极的,那么您的整个计划就进入了一个全新的水平.=(试着解释会给我一本书.但我可以指出一些你可以看到的设计模式:具体表继承(1类,1表),单表继承(N类,1表)和类表继承(N课程,M表).

我可以在这里深入探讨许多不同的观点,但是ORM通常会导致头部爆炸.我现在就停下来.

PS:我是Doctrine ORM的核心开发人员之一.除非你出于学习目的这样做,否则不要费心去创造另一个.这是一个非常复杂,耗时的工作,在你编写第一行代码之前需要进行大量的事情规划.事实上,我们计划了2年的Doctrine ORM,用了1年的时间来可靠地实现核心功能.我并不沮丧你,但正如福勒在他的ORM仇恨文章中所说,对于一个复杂的问题来说,这是一个复杂的解决方案.


Gor*_*don 3

我期待着您能得到有关此主题的任何答案,但与此同时,为什么不跳到亚马逊(或您当地的图书经销商)并最终购买

这些书包含您在各种问题中指出的原始模式,并且被视为设计模式和软件架构的参考书。