我差不多完成了我的数据映射器,但现在我已经处理了关系。
我会尝试在这里说明我的想法。我无法找到关于这个主题的好文章/信息,所以也许我正在重新发明轮子(我确定,我可以使用一个大框架 - 但我想通过这样做来学习)。 / p>
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关系更大的问题吗?
如果有人知道有关解决/实施此问题的详细信息,我会对链接感到高兴!
答案 0 :(得分:16)
在我开始之前,我假设你从头到尾都从福勒读过PoEAA书。 =) 此外,我认为您已经考虑过在处理ORM时遇到的第一个初始问题。我可以突出显示一个简单的方法,例如使用相同的标识符多次调用DataMapper并始终返回相同的对象(读作IdentityMap)。
重要提示:数据映射器是否允许使用其他数据映射器?
如果第二个是第二个弱引用,则只能让一个DataMapper访问另一个DataMapper。
让我们说在大多数情况下,您需要公司对象和地址对象,因为您始终将它一起显示在列表中。在这种情况下,CompanyDataMapper不仅可以获取公司对象,还可以使用JOIN进行SQL查询以获取地址对象的所有字段。最后,它迭代记录集并使用相应的值提供新对象,将地址对象分配给公司对象。
你在这里尝试讨论的问题在实践中听起来很简单,但在幕后却有点复杂。
首先,您不应该拥有getAddress(公司),而是拥有代理对象。 代理是给定实例的非初始化表示。在这种情况下,代理服务器包含您要查找的条目的引用。它必须从原始对象扩展,并需要提供初始化方法,以及相关的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仇恨文章中所说,对于一个复杂的问题来说,这是一个复杂的解决方案。
答案 1 :(得分:3)
我期待您对此主题的任何答案,但与此同时,为什么不只是跳到亚马逊(或您当地的书籍经销商)并最终购买
这些书包含您在各种问题中指出的原始模式,并被视为设计模式和软件架构中的参考书。
答案 2 :(得分:0)
我也正在解决这个问题。首先,我调整了Matt Zandstra的 PHP对象,模式和实践(2d Ed)中的数据映射器模式。我现在看到new edition出现了
也许设置中最巧妙的部分是“集合”对象。我不确定你使用的语言是什么,所以我会给你详细的信息。我只想说PHP有一个Iterator接口,它可以在一开始就加载一个数组(地图,在其他语言中),并在循环时将原始数据转换为对象(水合物?)。
和你一样,我正在努力解决如何加载关系的问题。到目前为止我发现的是我可以在Mapper类中编写我的大量JOIN查询,并为目标对象创建一个脱水集合,同时潜入相关对象的数据。
我真的不喜欢“Lazy Load”,因为它导致了如此多的数据库查询。它冒犯了我的完美主义情绪,因为我知道我正在使用数十个或数百个查询来实现这一目标。
我也期待更多答案。