【问题标题】:Handling relationships with the Data Mapper pattern使用数据映射器模式处理关系
【发布时间】:2012-11-23 09:02:06
【问题描述】:

我正在使用数据映射器模式,我想知道处理与该模式的关系的最佳方法是什么。我花了很多时间在 Google 和 Stack Overflow 中寻找解决方案,我找到了一些,但我仍然对它们并不完全满意,尤其是在我将尝试解释的一种特殊情况下。

我正在使用 PHP,所以我将放置的代码示例都在 PHP 中。

假设我有一个表“team”(id,name)和一个表“player”(id,name,team_id)。这是一个 1-N 的关系。 通过实现 Data Mapper 模式,我们将拥有以下类:Team、TeamMapper、Player 和 PlayerMapper。

到目前为止,一切都很简单。如果我们想从一个团队中获得所有玩家怎么办?

我找到的第一个解决方案是在 Team 类中创建一个 getAllPlayers() 方法,该方法将通过延迟加载和代理来处理该问题。然后,我们可以像这样检索球队的球员:

$players = $team->getAllPlayers();

我找到的第二种解决方案是直接使用 PlayerMapper 并将团队 ID 作为参数传递。比如:

$playerMapper->findAll(array('team_id' => $team->getId()));

但是现在,假设我想显示一个包含所有球队的 HTML 表格,以及一个包含每个球队所有球员的“球员”列。如果我们使用我描述的第一个解决方案,我们将不得不执行一个 SQL 查询来获取球队列表,并为每个球队执行一个查询来获取球员,这意味着 N+1 个 SQL 查询,其中 N 是球队的数量。

如果我们使用我描述的第二种解决方案,我们可以首先检索所有团队 ID,将它们放入一个数组中,然后将其传递给玩家映射器的 findAll 方法,如下所示:

$playerMapper->findAll(array('team_id' => $teamIds));

在这种情况下,我们只需要运行 2 个查询。好多了。但我仍然对这个解决方案不太满意,因为模型中没有描述关系,开发人员必须了解它们。

所以我的问题是:数据映射器模式还有其他替代方案吗?在我给出的示例中,有没有一种好方法可以在 2 个查询中选择所有包含所有球员的球队,并将关系描述到模型中?

提前谢谢你!

【问题讨论】:

    标签: php design-patterns orm datamapper


    【解决方案1】:

    如果您查看 Martin Fowler 描述 DataMapper 工作原理的文本,您会发现您可以使用一个查询来获取您需要的所有数据,然后将这些数据传递给每个映射器,从而允许映射器选择只有它需要的数据。

    对您来说,这将是一个从团队连接到玩家的查询,返回一个结果集,其中包含每个唯一玩家的重复团队数据。

    然后,您必须通过仅在数据更改时创建新对象来满足映射代码中的重复。

    我做了类似的事情,相当于团队映射器迭代结果集,并且对于每个独特的团队,将结果集传递给玩家映射器,以便它可以创建一个玩家,然后将玩家添加到团队的收藏。

    虽然这可行,但这种方法在下游存在问题......

    【讨论】:

    • 您指的是 Martin Fowler 的哪篇文章?我发现的唯一一个是那个 (martinfowler.com/eaaCatalog/dataMapper.html),我看不到你所说的任何内容。无论如何,这种方法并不能说服我。这意味着您必须执行一个查询,返回 NxM 行(其中 N 是球队数量,M 是球员数量),其中包含大量重复数据。这也意味着通过团队映射器映射玩家,这听起来不太好。正如你所说,我确信这种方法在下游存在问题......
    • 它在 PoEAA 书中。该网站仅提供概述。您的查询不返回 NxM 行,只返回 M 行。它确实带回了很多数据,但这就是重点。在一次往返中尽可能多地抓住。此外,您的团队映射器不会映射玩家,它会要求玩家映射器为其映射玩家。
    • 是的,抱歉,M 行。嗯,这是一个解决方案,但我仍然不完全相信。看看我的问题会不会带来更多答案……
    • @Vincent:你不完全相信,但不要说为什么-没有接受答案,这个评论对那些未来寻求的人没有用。
    • @DavidOsborne:下游有什么问题?
    【解决方案2】:

    我已经在我的一个项目中成功实施了这个问题的可能解决方案。它并不复杂,在上述示例中只使用 2 个查询。

    解决方案是添加另一层负责处理关系的代码。

    例如,我们可以将它放在一个服务类中(它也可以用于其他东西,而不仅仅是处理关系)。 因此,假设我们在 Team 和 TeamMapper 之上有一个 TeamService 类。 TeamService 将有一个方法 getTeamsWithRelationships() 将返回一组 Team 对象。 getTeamsWithRelationships() 将使用 TeamMapper 获取团队列表。然后,使用 PlayerMapper,它将只查询这些球队的球员列表,并使用 Team 类中的 setPlayers() 方法将球员设置为球队。

    此解决方案非常简单且易于实施,并且适用于所有类型的数据库关系。我想有些人可能会反对它。如果是这样,我很想知道有什么问题?

    【讨论】:

      猜你喜欢
      • 2013-11-07
      • 2014-06-27
      • 2010-09-17
      • 1970-01-01
      • 1970-01-01
      • 2013-06-13
      • 2013-02-11
      • 2015-04-01
      • 2014-10-26
      相关资源
      最近更新 更多