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


Doctrine 2.5 Unexpected association fetch behavior [Symfony 3]

我有 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 

如您所见,一切都将迫不及待地获取。

我需要什么?

我想使用单个查询仅检索帖子集以及用户

和 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();
    

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

编辑

枯萎的答案对于原始帖子是可以的。我没有以正确的方式暴露我的问题。我告诉我想检索 VIP 信息,因为我认为这是摆脱我上面讨论的额外查询的好方法。实际上我不需要 VIP 信息,但省略->leftJoin('u.vip','v')->addSelect('v')会使教义发出收集 VIP 信息的额外查询!有没有办法阻止教义执行此查询?

Doctrine2 中有两种类型的连接查询:

1) 定期加入
2) 获取联接

查看文档第 14.2.2 章。加入以获取更多详细信息。

因此,如果要获取加入 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 将是摆脱额外查询的最佳方法

您无法

摆脱额外的查询,因为您无法延迟加载一对一关系的反面。有关更多详细信息,另请参阅此帖子

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

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

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