我有一个查询如下:
我的用户实体有一个一对一的关系,看起来像这样:
/**
* @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) 在find
、findAll
等函数中,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;