我有一个如下所示的查询:
我的用户实体具有一对一的关系,如下所示:
/**
* @var UserProfile
*
* @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user")
*/
private $userProfile;
每当我进行查询以选择多个用户对象时,它会为每个用户创建一个额外的select语句来查询UserProfile数据,即使我没有通过get方法访问它。我并不总是需要UserProfile数据,我当然不希望每次显示用户列表时都加载这些数据。
知道为什么在运行时执行这些查询吗?
答案 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)在find
,findAll
等函数中,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。 要解决此问题,目前必须执行查询以查找此信息。
答案 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;