数据映射器模式和关系数据

假设我有以下课程:

User (域对象) UserMapper (数据映射器) Achievement (域对象) AchievementCollection (域对象) AchievementMapper (数据映射器)

这里的关系是一个User有一个AchievementCollection作为一个属性(或者,一个User有很多Achievements )。

我希望能够做的是查询用户列表,并且也查询他们所有的成就。 但是,我不完全确定如何使用数据映射器模式执行此操作。 如果我循环遍历每个用户并使用AchievementMapper查询用户的成就,那么效率相当低,特别是如果我有50多个用户查询。

处理这种情况的最佳方法是什么? (另外,这是为了学术/学习目的,这就是为什么我不使用Doctrine)

UserMapper是否应负责获取用户的成就? 我应该只使用成就映射器来查询userID的所有成就吗? (并通过每个用户循环?)


使用ORM(即使你自己编写的,所以任何将对象一般映射到关系表的东西,反之亦然)总是一个折中:当然有优点,但也有缺点。

ORM和写定制查询之间的主要折衷是易用性和开发速度与效率的关系。 换句话说,好处是,例如,你突然开始写$userMapper->getUserById(5); 并获得相关的User对象作为回报。 查询的组成和执行,结果集的获取以及对象的映射都是为您完成的。 缺点是,除了最基本的用例之外,ORM都不会执行最佳(组合)查询来实现目标。 作为牺牲的回报,你可以更容易地使用(作为程序员)和更快的开发(尽管ORM也能阻止你......)。

一般来说,当使用ORM时,你(尽力)忘记了效率较低的问题。 如果您尝试创建一个与自定义SQL查询效率相同的ORM,则最终将重新创建SQL。

在你只有50个用户的例子中,我只会使用ORM,并且对每个用户查询一次成就表效率低下。 任何体面的数据库服务器都不会有任何问题。

不过,既然你提到了学术方面,我想出了一些改进每次通话查询方案的选项。 对于您的特定用例,您可以尝试某种形式的缓存:预取所有Achievement对象,将该集合标记为“完成”(即映射器可以假定数据库中没有尚未加载的其他Achievement )和然后使$user->getAchievements()检查是否有一个“完整的”成就集合,如果是,则使用该集合而不是数据库。 从外部的角度来看,这可能是这样的:

$achievementMapper->preloadAll();
foreach ($userMapper->getAll() as $user) {
    echo "User {$user->getName()} has the following achievements: ";
    foreach ($user->getAchievements() as $achievement) {
        echo $achievement->getName();
    }
}

在内部,理想情况下,它只执行两个查询:一个选择所有成就,一个选择所有用户。 用户和成就的加入由ORM在内存中完成。 此方法的主要缺点是内存使用率高,并且对某种类型的所有对象不感兴趣时​​效率非常低(例如,只有拥有五个以上成就的用户)。

一个更复杂的选项是根据您选择的用户预加载成果,这可能看起来像这样:

$top100Users = $userMapper->getTop100();

// internally cache all Achievement objects linked to any of the 100 users
$achievementMapper->preloadByUserCollection($top100Users) 

使用这种方法,可能会降低效率,但是(记住,总是折衷)ORM的使用和ORM本身的使用变得更加复杂。 例如,实现映射器必须记住哪个用户已经预装了所有成就,并且如果不是这种情况(或者自那时起数据库已经改变)仍然到数据库。

另一个(更复杂的)选项是像Doctrine的DQL提取连接这样的机制,在这里你告诉ORM它应该从查询的结果集中'映射'哪个不同的对象(类型)。 ORM的不必写查询部分随此消失,但结果集自动映射到具有正确关联的对象仍然存在。

链接地址: http://www.djcxy.com/p/9619.html

上一篇: Data mapper pattern, and relational data

下一篇: Data Mapper, only for CRUD operations?