假设我的Symfony2项目中有两个实体:Category
和Article
(一个包含很多文章的类别)。
在我的CategoryRepository
中,我有这个方法:
findAllDummy(){
return $this->createQueryBuilder('c')
->leftJoin('c.Articles a')
->getQuery()->getResult();
}
如果我记得很清楚,在Symfony1.4(以及相应的Doctrine版本)中,返回的对象的“articles”属性将由相应的Article
对象填充。
现在,在Symfony2中,返回代理对象。
因此,如果我遍历特定类别的文章,将执行与迭代一样多的查询。
foreach($category->getArticles() as $article){
echo $article->getDoctrine()
->getRepository('')getTitle();
}
据我所知,这是Doctrine2.1的默认延迟加载行为。
问题1:这是一个更好的解决方案? N个查询而不是1个。
我尝试通过执行以下操作来强制执行加载:
findAllDummy(){
return $this->createQueryBuilder('c')
->leftJoin('c.articles a')
->getQuery()
->setFetchMode('Category', 'articles', 'EAGER')
->getResult();
}
但结果仍然相同。
问题2:如何在Doctrine2中强行加载?
答案 0 :(得分:18)
你正在加入一张桌子,但你没有从中选择任何东西。将->addSelect('a')
添加到查询构建器。考虑以下两个SQL查询来理解差异:
SELECT a.id, a.title
FROM article a
JOIN category c ON a.category_id = c.id
WHERE a.id = 123;
SELECT a.id, a.title, c.id, c.name
FROM article a
JOIN category c ON a.category_id = c.id
WHERE a.id = 123;
渴望/懒惰加入与DQL查询无关。它定义了使用$articleRepository->find(123)
时应该加载的内容。
答案 1 :(得分:2)
在您尝试“强制加载”的部分中,问题可能是您使用p+n
方法使用错误的变量类型fetchMode
参数。传递字符串$fetchMode
但该方法不期望字符串而是整数。
该方法需要来自'EAGER'
类的常量:
ClassMetadata
在Doctrine文档章节 14.7.6.6中。暂时更改DQL中的获取模式,您可以看到有关如何使用它的示例:
/**
* Specifies that an association is to be fetched when it is first accessed.
*/
const FETCH_LAZY = 2;
/**
* Specifies that an association is to be fetched when the owner of the
* association is fetched.
*/
const FETCH_EAGER = 3;
因此传递对常量的引用或对应于您要使用的模式的整数。
答案 2 :(得分:0)
正如doctrine docs中所述,在这种情况下,急切加载不会有任何区别,因为你在类别和文章之间有一对多的关系。
对于一对多关系,将获取模式更改为eager将导致为每个加载的根实体执行一个查询。与惰性提取模式相比,这没有任何改进,一旦访问它们,它也将逐个初始化关联。
与@Crozin所说的相反,你仍然可以在DQL中进行急切加载。
如果您具有一对一或多对一关系,则急切加载将解决进行额外查询的问题。但是,要在这种情况下解决您的问题,您应该使用->addSelect('a')
作为@Crozin提及。
答案 3 :(得分:-2)
这是一个更好的解决方案,因为连接是一个比简单查询更昂贵的过程。虽然它看起来效率低下,但它并不是很浪费,当你没有加载每个相关对象的每一点时,它会很快变得更有效率。