【问题标题】:Force waterfall hydration on getResult在 getResult 上强制瀑布水合作用
【发布时间】:2014-08-15 02:44:33
【问题描述】:

我有一个名为“视频”的实体,附加到它的是由 ManyToOne 和 ManyToMany 关联的其他实体。

我的问题是,正在执行的查询数量对正常流量感到不舒服。例如,我在首页上执行了可变数量的查询,大约有 49 个查询。

正在执行的绝大多数查询是因为我的视频实体的子实体在调用时没有自动初始化。转储时,我看到私有属性“is_initialized”设置为 0。当在我的模板中调用时,由于它们往往被报告,Doctrine 然后执行查询以获取该数据。

我明白 Doctrine 为什么要这样做,如果没有使用该数据,为什么首先要获取它?在大多数情况下,这种行为很好。

但就我而言,我的VideoRepository 类中有一些方法可以获取特定的视频集。所以我在这里使用 DQL。作为回应,我决定在 DQL 中将结果缓存设置为 Apc。

这是来自存储库的一个相对无害的示例

    /**
     * Get Videos based on recently submitted
     *
     * @param null $limit
     * @param int $page
     * @return array
     */
    public function latest($limit = null, $page = 1)
    {
        $hash = md5(sprintf("%s_%s", $limit, $page));

        $query = $this->getEntityManager()->createQueryBuilder();
        $query->select('v')->from($this->getEntityName(), 'v')
            ->where('v.published = :published')
            ->orderBy('v.datePublished', 'DESC')
            ->setParameter('published', true);

        if($limit) {
            $query->setFirstResult((int)(($page-1) * $limit));
            $query->setMaxResults($limit);
        }

        $resultQuery = $query->getQuery();
        $resultQuery->setResultCacheDriver(new ApcCache())->useResultCache(true, 300, sprintf('results_latest.%s', $hash));

        return $resultQuery->getResult();
    }

因此,在首页(正在执行 49 个查询,一旦生产数据开始出现更多查询),此方法请求了 10 个视频。

每个视频内部都有一个关联的“产品”实体 (ManyToMany)。在每个视频的缩略图显示中,都有一个小的产品图标,因此我自然需要从视频实体访问包含产品实体的$product 属性。

此时,它又执行了 10 个查询,因为 Doctrine 从不费心为每个视频获取 Product 数据,因此它为我要求的每个图标创建一个查询,这使我们最多 11 个(最少,ManyToMany 意味着每个视频可以与多个产品相关联)。此页面上还有 3 条视频查询,用于不同的列表,使用 VideoRepository 中的不同方法

这里的问题是,虽然缓存系统将每个主查询的结果数据存储 5 分钟,但它还隐藏了尚未获取产品和其他嵌入式实体。

所以用简单的数学计算,这意味着使用缓存系统只为我节省了 4 个查询,使我的 Doctrine 查询计数达到 45 个。

在使用 getResult 时,是否有一种简单的方法告诉 Doctrine 对所有子实体进行子查询和自动水合?

【问题讨论】:

  • 左加入产品数据将导致它在初始查询中加载。您还可以在您的关联(甚至全局)上设置 fetch=eager,但您可能并不想这样做。 doctrine-orm.readthedocs.org/en/latest/reference/…
  • 嗯,当我将products 属性设置为Eager 时,执行查询时似乎没有缓存结果:\
  • 我自己不使用 Doctrine 缓存的东西,所以我无法帮助你。您的标签表明您正在使用 Symfony 2,因此您可以尝试它们的缓存。它相当广泛。并且不要忘记在更改 Doctrine 关联时清除 Symfony 缓存。

标签: php mysql symfony caching doctrine-orm


【解决方案1】:

是的,更改您的查询以选择更多实体,而学说将需要更少的延迟加载。

$query->select('v', 'sub', 'others')
        ->from($this->getEntityName(), 'v')
        ->leftJoin('v.subtitles','sub')
        ->leftJoin('v.others','others')
        ->where('v.published = :published')
        ->orderBy('v.datePublished', 'DESC')
        ->setParameter('published', true);

这将返回更多数据,并且每次您需要字幕或其他实体时,学说不会有连续的查询。作为警告,这将返回更多数据,因此请确保此方法是可接受的。

【讨论】:

  • 我认为这应该是可以接受的,只是很遗憾它必须明确指定连接,而不是启用设置来自动执行瀑布水合或深度水合
  • 全局设置(Doctrine 2 确实存在)的问题是最终会意外加载整个数据库。
  • 我做了一个组合这个和一些很好的fetch="Eagers",但不幸的是leftJoin本身并没有水合实体。我最终只是使用 DQL 来获取所有结果,根据需要的信息进行水合和调用,并使用 Doctrine2 提供的 ApcCache 驱动程序来存储最终结果。似乎工作得很好!
  • 如果您要了解这么多细节,并且要求性能,您可能希望执行部分查询以仅从您正在查询的实体中选择特定字段。只有当您有一个不需要的字段并且包含大量数据时,它才会有所作为。我的一张桌子上有几何图形,有些几何图形有数百个点。使用 partials 大大加快了我的查询速度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 2017-11-10
  • 2020-01-14
  • 1970-01-01
相关资源
最近更新 更多