【问题标题】:How does Doctrine2 One-To-Many fetch=EAGER work?Doctrine2 一对多 fetch=EAGER 是如何工作的?
【发布时间】:2015-12-11 04:35:28
【问题描述】:

我正在使用 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 来说,每个查询的这种 fetch 方法的手动设置似乎不可用,但我不知道 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="EAGER" 标记一对多关联时,它现在会 比以前少执行一个查询,并在组合中正常工作 使用 indexBy。

完全不清楚预期的行为是什么。

欢迎任何见解,谢谢!

【问题讨论】:

  • 您使用的是哪个版本的 Doctrine?应该是 2.5。
  • 我正在使用学说 orm v2.5.2。
  • 如果你使用 queryBuilder 来检索你的对象,你应该使用 leftJoin,而不是 fetch 策略:/ 你是否也尝试过使用普通 ->findAll() 和你的循环文件?

标签: symfony doctrine-orm


【解决方案1】:

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

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

你有两个选择

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

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

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

... 对于一对多关系,将获取模式更改为渴望将导致对加载的每个根实体执行一个查询。这对惰性获取模式没有任何改进,惰性获取模式也会在关联被访问后逐一初始化。

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

通过一次查询获取库实体并延迟加载文件实体。

  • 这是 Doctrine 的默认行为 (fetch="LAZY"),对于您访问的每组 Gallery#$files,将产生 1 个查询和一个附加查询(如果您访问它们,则为 1+n 个查询)。

未来可能的选择

有一个 PR to add an EAGER_BATCHED fetch option 可以完全满足您的要求(使用一个查询获取 Gallery 实体,然后使用第二个查询获取所有 File 实体)但似乎并没有发生太多事情,不幸的是。

结论

如果您使用->find* 方法,fetch="EAGER" 注释将受到尊重。 Doctrine 将使用 LEFT JOIN 来执行此操作。根据您的数据集,这可能没问题或非常昂贵。

如果您要手动编写 DQL 或使用查询构建器,您必须 LEFT JOIN 一对多关系并记住将您想要获取的任何实体添加到 select 子句。

我的偏好是在您想要急切地获取一对多关系时编写 DQL,因为它使您的意图更加明确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-30
    • 2011-09-24
    • 1970-01-01
    • 2011-07-16
    • 2014-03-27
    • 1970-01-01
    相关资源
    最近更新 更多