Doctrine 2.5 意外的关联获取行为 [Symfony 3]

Posted

技术标签:

【中文标题】Doctrine 2.5 意外的关联获取行为 [Symfony 3]【英文标题】:Doctrine 2.5 Unexpected association fetch behavior [Symfony 3] 【发布时间】:2016-11-10 03:36:41 【问题描述】:

我有 3 个以这种方式关联的实体:

别担心,我已经使用注释设置了关联,但我认为以下组合会更轻松/更清晰地暴露我的问题

Post
  @ORM\ManyToOne(targetEntity="User", fetch="EAGER")
  - author
User
  @ORM\OneToOne(targetEntity="Vip", mappedBy="user", fetch="EAGER")
  - vip
Vip
  # Notice that the primary key of vip is a foreign key on User primary
  @ORM\id
  @ORM\OneToOne(targetEntity="User", inversedBy="peliqan", fetch="EAGER")
  @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
  - user 

如您所见,一切都已准备就绪。

我需要什么?

我想仅使用 单个查询检索帖子集以及 both 用户 & Vip 信息>。 (见编辑)

现在,对于每个帖子条目,我都会得到一个额外的查询:

SELECT t0.valid AS valid_1, ...
FROM vip t0
INNER JOIN user t10 ON t0.user_id = t10.id WHERE t0.user_id = ?

何时:

我执行这个

$results = $qb
    ->select('ps')
    ->leftJoin('ps.author','u')->addSelect('u')
    ->add('where', $qb->expr()->in('ps.id', $ids))
    ->getQuery()->getResult();

即使我尝试像这样强制执行 FETCH_EAGER 模式

->getQuery()
->setFetchMode('AppBundle\Entity\User', 'vip', ClassMetadata::FETCH_EAGER)
->getResult();

注意: 我设法摆脱了enforcingQuery::HYDRATE_ARRAYgetResult() 呼叫时的额外查询。 查询消失了,节省了初始时间的三分之一。 这里的缺点是,在以数组形式检索关联时,我无法再利用Symfony\Component\Serializer\Annotation\Groups 来过滤实体属性,并且必须手动编辑结果集才能删除/转换某些值。

编辑

原始帖子的威尔特答案是可以的。我没有以正确的方式揭露我的问题。我告诉我要检索 Vip 信息,因为我认为这是摆脱我上面所说的额外查询的好方法。其实我不需要 Vip 信息,但省略->leftJoin('u.vip','v')->addSelect('v') 使学说发出额外的查询来收集Vip 信息!有没有办法阻止学说执行这个查询?

【问题讨论】:

【参考方案1】:

Doctrine2 中有两种连接查询:

1) 常规连接 2) 获取连接

查看the documentation chapter 14.2.2. Joins了解更多详情。

因此,如果您想获取加入 vip,您应该在查询中 addSelectleftJoin 他们如下:

$results = $qb
    ->select('ps')
    ->addSelect('u')->leftJoin('ps.author','u')
    ->addSelect('v')->leftJoin('u.vip','v')
    ->add('where', $qb->expr()->in('ps.id', $ids))
    ->getQuery()->getResult();

更新

评论后更新:

我认为在结果集中包含 vip 将是摆脱额外查询的最佳方法

您无法摆脱额外的查询,因为您无法延迟加载一对一关系的反面。另请参阅 this post 了解更多详情:

这是预期行为。从技术上讲,一对一关联的反面不能偷懒。反面没有外键,因此无法决定是否代理它。我们必须查询关联的对象或加入它。请注意,这只影响单值关联的反面,即实际上只影响双向一对一关联的反面。

一种解决方案可能是反转关系,使用户成为关系的拥有方。在这种情况下,您至少可以在 User 实体中延迟加载 Vip。延迟加载问题将转移到Vip 方面,这意味着您不能再将lazy-load User 放在Vip 中。

否则,您可以让查询返回 Partial object 以防止加载 Vip,但通常您应该非常小心这种方法。

【讨论】:

感谢您的宝贵时间。其实这是我知道的。我没有以正确的方式暴露我的问题。我认为在结果集中包含 vip 将是获得 额外查询 的最佳方式。我希望我不会在这里弄得一团糟。一次又一次地为你点赞,谢谢。 我或许应该再提出一个问题…… 事情对我来说越来越清楚了,我需要发现一个cannot lazy load the inverse side of a one-to-one relationship。我已阅读拥有方必须是持有外键的实体。这就是我将 Vip 设为拥有方的原因(它的 PK 是指向 User id PK 的外键)。最后,获取我需要的信息的最轻松/最快的方法是强制执行 Query::HYDRATE_ARRAY。我猜想防止对象水合,然后没有额外的查询来解决关系的反面。我仍然必须处理 mysql 延迟行查找问题现象。

以上是关于Doctrine 2.5 意外的关联获取行为 [Symfony 3]的主要内容,如果未能解决你的问题,请参考以下文章

Doctrine 不能正确地从会话中加载关联

Doctrine em 在 Codeigniter 中获取关联数据

Doctrine 2.1.6 逆向工程 1-n(一对多)关联被误解为 1-1(一对一)

教义:在 OneToMany 关联中忽略了 mappedBy 的值

通过findOneBy方法获取的Doctrine实体的行为与通常类似,但会触发致命错误

意外的异步行为:Springs 的 @Async 与 RxJava