教义2.5意外的关联提取行为[Symfony 3] [英] Doctrine 2.5 Unexpected association fetch behavior [Symfony 3]

查看:127
本文介绍了教义2.5意外的关联提取行为[Symfony 3]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



不用担心,我已经使用注释设置了关联,但是我认为以下组合会更轻/更干净公开我的问题

 发布
@ ORM\ManyToOne(targetEntity =User,fetch =EAGER )
- 作者
用户
@ ORM\OneToOne(targetEntity =Vip,mappedBy =user,fetch =EAGER)
- vip
vip
#请注意,vip的主键是用户主键
@ ORM\id
@ ORM\OneToOne(targetEntity =User,inversedBy =peliqan)的外键,fetch =EAGER)
@ ORM\JoinColumn(name =user_id,referencedColumnName =id,nullable = false)
- 用户

正如你所看到的,所有这些都被设置为热切地抓取。



我需要什么?



我想要检索帖子集以及两者 Users & Vip 信息仅使用 单一查询



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

  SELECT t0.valid AS valid_1,... 
FROM vip t0
INNER JOIN用户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_ARRAY getResult()调用。

查询消失,其中保存了初始时间的三分之一。

这里的缺点是在将关联检索为数组时,我无法利用 Symfony\Component\Serializer\Annotation\Groups 任何更多的过滤实体属性,必须手动编辑结果集才能删除/转换一些值。



编辑



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

解决方案

Doctrine2中有两种连接查询:



1) 常规连接

2)加入



检查 14.2.2中的dql-doctrine-query-language.html#joinsrel =nofollow>。加入 更多细节。



所以如果你想获取加入vips,你应该 addSelect leftJoin 它们在您的查询内,如下所示:

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





更新



在您的评论后更新:


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


你不能摆脱额外的查询,因为你不能懒惰加载相反的一面一对一的关系。另请参阅 此帖 了解详情:


这是预期的行为。一对一协会的反面不能在技术上懒惰。反面没有外键,因此无法决定是否进行代理。我们必须查询关联的对象或加入它。请注意,这只会影响单值关联的反面,也就是说,只是双向一对一关联的反面。





  • 解决方案可能是反转关系,从而使用户成为关系的拥有者。我可以在您的用户实体内至少懒惰加载 Vip 。懒惰加载问题会移动到 Vip 侧,这意味着您不能懒惰加载您的 Vip 中的用户


  • 否则你可以让你查询返回部分对象为了防止加载 Vip ,但一般来说,您应该非常小心这种方法。



I have 3 entities associated this way:

Don't worry, I've set associations using annotations, but I thought the following mix would be lighter/cleaner to expose my issue

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 

As you can see, all is set to be eagerly fetched.

What do I need?

I would like to retrieve Posts sets along with both Users & Vip infos ONLY, using a single query. (see edit)

Right now, for every post entry I get one extra query:

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

when:

  • I execute this

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

  • and even while I try to enforce FETCH_EAGER mode like this

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

Note:
I managed to get rid of extra query by enforcingQuery::HYDRATE_ARRAY upon getResult() call.
Queries vanished which saved a third of the initial time.
The downside here is that while retrieving association as arrays, I could not take advantage of Symfony\Component\Serializer\Annotation\Groups any more to filter entities properties and had to manually edit result set in order to remove/transform some values.

EDIT

Wilt answer is okay for the original post. I did not expose my issue the right way. I told I want to retrieve Vip infos because I thought it was a good way to get rid of the extra query I talk above. Actually I do not need Vip infos but omitting ->leftJoin('u.vip','v')->addSelect('v') makes doctrine issue the extra query which gather Vip infos! Is there a way to prevent doctrine from executing this query?

解决方案

There are two kinds of join queries in Doctrine2:

1) Regular joins
2) Fetch joins

Check the documentation chapter 14.2.2. Joins for more details.

So if you want to fetch join vips you should addSelect and leftJoin them inside your query as follows:

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


UPDATE

Update after your comment:

I thought including vip in the result set would be the best way to get rid of the extra query

You cannot get rid of the extra query because you cannot lazy load the inverse side of a one-to-one relationship. Refer also to this post for more details:

This is expected behavior. Inverse sides of one-to-one associations can not be lazy, technically. There is no foreign key on the inverse side, hence it is impossible to decide whether to proxy it or not. We must query for the associated object or join it. Note that this only affects inverse sides of single-valued associations, that is, really only the inverse side of bidirectional one-to-one associations.

  • A solution could be to inverse the relationship so user becomes the owning side of the relationship. I that case you can at least lazy-load Vip inside your User entity. The lazy load problem would move to the Vip side, meaning you could not lazy-load your User in Vip any longer.

  • Otherwise you could make your query return a Partial object to prevent loading of Vip, but in general you should be very careful with this approach.

这篇关于教义2.5意外的关联提取行为[Symfony 3]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆