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



我有一个查询如下:

我的用户实体有一个一对一的关系,看起来像这样:

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

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

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

以下是详细解释的解决方案:

https://groups.google.com/forum/#!主题/条令用户/fkIaKxixDqc

映射中的"fetch"是提示,也就是说,如果可能的话学说确实做到了这一点,但如果不可能的话,显然也不可能。从技术上讲,代理延迟加载并不总是可能的。不可能的情况有:

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

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

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

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

2) 强制部分对象。没有其他查询,但也没有延迟加载:$query->setHint(query::HINT_FORCE_PPARTIAL_load,真)

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

Benjamin对这些货物的自动配料有一些想法避免n+1个查询,但这不会改变代理是并不总是可能的。

我花了很多时间搜索解决方案。对我来说,没有一个选项足够令人满意,但也许我可以用以下解决方法为某人节省一些时间:

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

2) 在findfindAll等函数中,OneToOne中的反面会自动连接(这总是像fetch EAGER一样)。但在DQL中,它不像获取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关闭了问题

这似乎是Doctrine中的一个开放问题,另请参阅

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

如果Doctrine检测到你正在获取反向一对一关联,它必须执行一个额外的查询来加载这个对象,因为它无法知道是否没有这样的对象(设置为null),或者它是否应该设置一个代理以及这个代理有哪个id。为了解决目前的这个问题,必须执行一个查询来找出这些信息。

正如@apfelbox所解释的。。。现在没有解决办法。

我选择了一个OneToMany解决方案,它与唯一的密钥相结合:

User.php
/**
 * @ORMOneToMany(targetEntity="TBUserBundleEntitySettings", fetch="EXTRA_LAZY", mappedBy="user", cascade={"all"})
 */
protected $settings;
/**
 * @return DoctrineCommonCollectionsCollection
 */
public function getSettings()
{
    return $this->settings;
}

Settings.php
/**
 * @ORMManyToOne(targetEntity="TBUserBundleEntityUser", fetch="EXTRA_LAZY", inversedBy="settings")
 * @ORMJoinColumn(name="user_id", referencedColumnName="id", nullable=false)
 */
protected $user;

为了确保Settings.php中的唯一性,包括:

use DoctrineORMMappingUniqueConstraint;

并添加唯一索引

/**
 * @ORMEntity
 * @ORMTable(name="user_settings", uniqueConstraints={@UniqueConstraint(name="user", columns={"user_id"})})
 */
class Settings

所以当我想访问用户设置时,我只需要这个(它只会在特定时刻触发一个查询)

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

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

还有另一个选项(这是最好的IMHO)-您可以使用单向OneToOne。

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

/**
 * @var User
 *
 * @ORMOneToOne(targetEntity="User")
 */
private $user;

只是不要在User中映射它。你可以在需要的时候加载它。

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

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

/**
 * @var UserProfile
 *
 * @ORMOneToOne(targetEntity="UserProfile",mappedBy="user", fetch="LAZY")
 */
private $userProfile;

相关内容

  • 没有找到相关文章

最新更新