Doctrine2 One-To-Many fetch = EAGER是如何工作的?

时间:2015-12-11 04:35:29

标签: symfony doctrine-orm

我正在使用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中实现的吗?

latest doctrine changelog州:

  

用fetch =&#34标记一对多关联时; EAGER"它现在会   执行一个比以前少的查询,并在组合中正常工作   使用indexBy。

根本不清楚预期的行为是什么。

欢迎任何见解,谢谢!

1 个答案:

答案 0 :(得分:2)

经过大量搜索和一些测试(在Doctrine ORM 2.5.6上使用PHP 5.6)后,我得到了一些结果。

目前,您无法通过一个查询获取Gallery个实体,也无法通过第二个查询获取所有相关File个实体。

您有两个选项

使用Left Join在一个查询中获取图库和文件实体。

  • 这是->find*方法在您的注释上设置fetch="EAGER"时所执行的操作。
  • 您可以使用DQL手动执行此操作:SELECT g, f FROM Gallery g LEFT JOIN g.files f

作为noted in the docs,您无法在DQL查询上调用->setFetchMode('Gallery', 'files', ClassMetadata::FETCH_EAGER)来获得相同的结果

  

...对于一对多关系,将获取模式更改为eager将导致为每个加载的根实体执行一个查询。与惰性提取模式相比,这没有任何改进,一旦访问它们,它也将逐个初始化关联。

这将导致在您第一次获取图库实体的查询后立即运行n个其他查询。

使用一个查询获取图库实体并延迟加载文件实体。

  • 这是Doctrine的默认行为(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,因为它会使你的意图清晰。