Doctrine2一对一关系自动加载查询

时间:2012-09-11 04:45:24

标签: doctrine-orm

我有一个如下所示的查询:

我的用户实体具有一对一的关系,如下所示:

/**
 * @var UserProfile
 *
 * @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user")
 */
private $userProfile;

每当我进行查询以选择多个用户对象时,它会为每个用户创建一个额外的select语句来查询UserProfile数据,即使我没有通过get方法访问它。我并不总是需要UserProfile数据,我当然不希望每次显示用户列表时都加载这些数据。

知道为什么在运行时执行这些查询吗?

6 个答案:

答案 0 :(得分:23)

以下是解决方案详细说明:

https://groups.google.com/forum/#!topic/doctrine-user/fkIaKxifDqc

  

“fetch”在映射中是一个提示,也就是说,如果可能的话   学说是这样做的,但如果它不可能,显然它不会。   从技术上讲,代表延迟加载并不总是可行的。   不可能的情况是:

     

1)从反向到拥有方一对一(仅出现在   双向一对一关联)。前提条件a)以上不能   得到满足。 2)与层次结构的一对一/多对一关联   目标类具有子类(不是类层次结构中的叶子)。   上述前提条件b)无法满足。

     

在这些情况下,代理在技术上是不可能的。

     

您可以选择避免此n + 1问题:

     

1)通过DQL获取连接:“从客户加入c.cart ca中选择c,ca”。   但是,单个查询但加入连接到一个关联是   相对便宜。

     

2)强制部分对象。没有其他疑问但是   也没有延迟加载:$ query-> setHint(Query :: HINT_FORCE_PARTIAL_LOAD,   真)

     

3)如果是替代结果格式(即getArrayResult())   对于用例来说足够了,这些也避免了这个问题。

     

本杰明对这些负载的自动批处理有一些想法   避免n + 1个查询,但这不会改变代理的事实   并非总是可能。

答案 1 :(得分:16)

我花了很多时间寻找解决方案。对我来说,没有一个选项足够令人满意,但也许我可以通过这个变通方法列表节省一些时间:

1)改变拥有方和反方http://developer.happyr.com/choose-owning-side-in-onetoone-relation - 我不认为每次从数据库设计的角度来看都是正确的。

2)在findfindAll等函数中,OneToOne中的反面自动连接(它总是像获取EAGER一样)。但是在DQL中,它不像fetch EAGER那样工作,并且需要额外的查询。可能的解决方案是每次加入逆实体

3)如果替代结果格式(即getArrayResult())足以满足某些用例,那么这也可以避免这个问题。

4)将反面改为OneToMany - 看起来不对,也许可能是一个临时的解决方法。

5)强制部分对象。没有其他查询,也没有延迟加载:$query->setHint (Query::HINT_FORCE_PARTIAL_LOAD, true) - 联系我唯一可能的解决方案,但不是没有价格: 部分对象有点冒险,因为您的实体行为不正常。例如,如果您未在->select()中指定您将使用的所有关联,则可能会出现错误,因为您的对象未满,所有未明确选择的关联都将为空

6)不映射反向双向OneToOne关联,并使用显式服务或更积极的记录方法 - https://github.com/doctrine/doctrine2/pull/970#issuecomment-38383961 - 看起来Doctrine关闭了问题

答案 2 :(得分:11)

这似乎是open issue in Doctrine,另见

  

4.7.1。为什么每次获取具有一对一关系的实体时都会执行额外的SQL查询?

     

如果Doctrine检测到您正在获取反向一对一关联,则必须执行另一个查询才能加载此对象,因为它无法知道是否没有此类对象(设置为null)或是否应该设置代理和此代理具有的ID。   要解决此问题,目前必须执行查询以查找此信息。

Source

答案 3 :(得分:0)

正如@apfelbox解释的那样......现在没有解决方法。

我选择了一个与唯一键组合的OneToMany解决方案:

Settings.php

/**
 * @ORM\ManyToOne(targetEntity="TB\UserBundle\Entity\User", fetch="EXTRA_LAZY", inversedBy="settings")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
 */
protected $user;

use Doctrine\ORM\Mapping\UniqueConstraint;

并确保Settings.php中的唯一性包括:

/**
 * @ORM\Entity
 * @ORM\Table(name="user_settings", uniqueConstraints={@UniqueConstraint(name="user", columns={"user_id"})})
 */
class Settings

并添加唯一索引

$_settings = $user->getSettings()->current();

因此,当我想访问用户设置时,我只需要这个(这将仅在特定时刻触发一个查询)

{{1}}

我认为这是最干净的解决方案。

答案 4 :(得分:0)

还有另一种选择(这是最好的恕我直言) - 你可以使用单向OneToOne。

在您的情况下 - 如果您很少使用UserProfile - 在UserProfile中设置链接

/**
 * @var User
 *
 * @ORM\OneToOne(targetEntity="User")
 */
private $user;

并且不要在用户中映射它。您可以在需要时加载它。

如果您经常使用UserProfile - 您可以将其作为用户实体的一部分。

答案 5 :(得分:-4)

根据reference,您可以添加可选属性fetch

/**
 * @var UserProfile
 *
 * @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user", fetch="LAZY")
 */
private $userProfile;
相关问题