我正在使用Symfony 2.8 / Doctrine ORM 2.5.2。
我有2个实体,Gallery
OneToMany File
class Gallery
{
/**
* @var File[]
*
* @ORM\OneToMany(targetEntity="File", mappedBy="gallery", fetch="EAGER")
*/
private $files;
}
我在文档中看到了两件事。
首先,现在OneToMany关系确实有fetch=EAGER
选项(specified here)。它在以前的版本中不存在。
其次,对于OneToMany,每个查询的此获取方法的手动设置似乎不可用,但我不知道the documentation是否是最新的,因为它指出:
在查询期间更改提取模式只能是一对一的 和多对一的关系。
无论如何我都试过了,这是我的疑问:
public function findWithEager()
{
$qb = $this->createQueryBuilder('g');
$query = $qb->getQuery();
$query->setFetchMode("CommonBundle\\Entity\\Gallery", "files", ClassMetadata::FETCH_EAGER);
return $query->getResult();
}
但是当我这样做时:
foreach ($galleryRepository->findWithEager() as $gallery) {
foreach ($gallery->getFiles() as $file) {
$file->getId();
}
}
然后我得到1 + n个查询。第一个是SELECT * FROM Gallery
,后面的n个是SELECT * FROM File WHERE id = :galleryId
我希望Doctrine做1 + 1个查询,第二个是SELECT * FROM File WHERE id IN (:galleryListIds)
我错过了什么吗?这种行为是在Doctrine中实现的吗?
用fetch =&#34标记一对多关联时; EAGER"它现在会 执行一个比以前少的查询,并在组合中正常工作 使用indexBy。
根本不清楚预期的行为是什么。
欢迎任何见解,谢谢!
答案 0 :(得分:2)
经过大量搜索和一些测试(在Doctrine ORM 2.5.6
上使用PHP 5.6
)后,我得到了一些结果。
目前,您无法通过一个查询获取Gallery
个实体,也无法通过第二个查询获取所有相关File
个实体。
使用Left Join
在一个查询中获取图库和文件实体。
->find*
方法在您的注释上设置fetch="EAGER"
时所执行的操作。SELECT g, f FROM Gallery g LEFT JOIN g.files f
作为noted in the docs,您无法在DQL查询上调用->setFetchMode('Gallery', 'files', ClassMetadata::FETCH_EAGER)
来获得相同的结果
...对于一对多关系,将获取模式更改为eager将导致为每个加载的根实体执行一个查询。与惰性提取模式相比,这没有任何改进,一旦访问它们,它也将逐个初始化关联。
这将导致在您第一次获取图库实体的查询后立即运行n个其他查询。
使用一个查询获取图库实体并延迟加载文件实体。
fetch="LAZY"
),会导致1个查询以及您访问的每组Gallery#$files
的附加查询(如果您访问它们,则会发出1 + n个查询) )。有一个PR to add an EAGER_BATCHED
fetch option可以完全按照您的要求执行(使用一个查询获取Gallery
个实体,然后使用第二个查询获取所有File
个实体)但是没有#t}不幸的是,似乎很多事情正在发生。
如果您使用->find*
方法,则fetch="EAGER"
注释将得到尊重。 Doctrine将使用LEFT JOIN执行此操作。根据您的数据集,这可能是正常的或非常昂贵。
如果您手动或使用查询构建器编写DQL,则必须LEFT JOIN
一对多关系,并记住将要提取的任何实体添加到select子句中。
我希望每当你想要急切地获取一对多关系时编写DQL,因为它会使你的意图清晰。