注意:主题冗长但详细,如果您使用Doctrine2和oneToOne关系,可能会派上用场。
最近我在Doctrine中遇到了一个问题:
我使用oneToOne双向关系创建了User和UserData对象:
User:
...
oneToOne:
userdata:
targetEntity: UserData
mappedBy: user
UserData:
...
oneToOne:
user:
targetEntity: User
inversedBy: userdata
所以UserData是拥有user_id列的拥有者:
user: id, ...
userdata: id, user_id, ...
这就产生了一个问题,每次你获取一个User对象(单个用户,用户集合或其他对象的集合,用户加入它)时,Doctrine会为每个用户延迟加载一个UserObject。
此处描述的问题:
此处描述的建议解决方案:
所以有三种方法:
我决定选择#3。所以我的架构关系现在看起来像这样:
User:
...
oneToOne:
userdata:
targetEntity: UserData
inversedBy: user
UserData:
...
oneToOne:
user:
targetEntity: User
mappedBy: userdata
这意味着我的表现在看起来像这样:
user: id, userdata_id, ...
userdata: id, ...
我决定不再使用Userdata.id自动增量,而是手动设置它并将其与user.id匹配。这意味着UserData.id将始终匹配user.id.
问题我可以使用user.id(主要自动增量密钥)作为joinColum而不是userdata_id,因为它们总是具有相同的值吗?您是否看到这种做事方式存在任何潜在问题?
非常欢迎和赞赏有关此问题的任何其他提示或意见。
答案 0 :(得分:18)
您还可以强制部分对象,以摆脱延迟加载:
use Doctrine\ORM\Query;
//...
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
答案 1 :(得分:14)
这是OneToOne关联的已知问题。关于此问题有github discussion值得一读。建议并拒绝解决方案(拉动请求)。
你的问题暗示了教条的贡献者提出的相同解决方案:改变关系的拥有方。
我与名为Version
的实体存在类似问题,该实体与OneToOne
具有Settings
双向关系。每次我查询Version
(例如10个特定版本记录)时,Doctrine都会对已加入的Settings
执行其他查询(就好像它是Lazy Loading这些实体一样)。即使我没有在任何地方引用Settings
,例如,也发生了这种情况。 $Version->getSettings()->getSomeProperty()
。
唯一的解决方案" (hack)对我有用的是每次我对Settings
进行查询时,为这个Version
实体手动包含一个JOIN。但由于我不需要额外的实体(在这种情况下),每次我以不同的方式查询此表时,这仍然是一个额外的不必要的查询。
基于其他建议,我认为如果我明确指定这种关系为"额外的懒惰"它会起作用,例如
/**
* @ManyToMany(targetEntity="Settings", mappedBy="version", fetch="EXTRA_LAZY")
*/
但这并不影响水合作用。 See the Doctrine Docs了解EXTRA_LAZY
所做的更多详情。
在我的情况下(当我不需要实体时)帮助的是指定我想要作为数组(而不是对象)获取。
$query = $queryBuilder->getQuery();
$query->getResult(Query:HYDRATE_ARRAY);
这将返回一个数组,因此它不会延迟加载OneToOne关联。但是,在我需要实体对象的其他环境中,我必须明确地加入实体(尽管不想要它)。
答案 2 :(得分:3)
我的解决方法是将OneToOne关系转换为ManyToOne(带有关联的ArrayCollection),使用自定义getter和setter,它只设置并返回集合的第0个元素:
/**
* Set pref
*
* @param \MyBundle\Entity\Pref $pref
* @return Employee
*/
public function setPref(\MyBundle\Entity\Pref $pref = null) {
$this->pref[0] = $pref;
return $this;
}
/**
* Get pref
*
* @return \MyBundle\Entity\Pref
*/
public function getPref() {
return $this->pref[0];
}
这些方法(看似)可以与OneToOne关系创建的方法相同。
doctrine:generate:entities
仍会创建正常的“添加”和“删除”方法,但可以忽略它们。
注意:我最近才开始使用这些,目前只从数据库中读取。我不知道此解决方法有任何副作用。如果我注意到,我会更新这个答案。
这无疑是一个丑陋的黑客。我希望Doctrine尽快解决这个问题,以便我可以回到使用正确的OneToOne关系。
答案 3 :(得分:0)
我遇到了同样的问题,并记住symblog教程给出了一个示例,说明如何通过在不需要的表上显式添加左连接来减少延迟加载。当你根本不想要那些数据时,在连接中包含表格似乎很奇怪,但是这样你就可以将所有额外的查询减少到1并且运行得更快。
搜索延迟加载 - 大约1/5向下 http://tutorial.symblog.co.uk/docs/customising-the-view-more-with-twig.html
要解决用户/用户数据问题,请尝试将其添加到用户存储库,即使您不需要userdata,也可以在需要获取所有用户时使用。通过选择部分可以进一步增强: - > select('partial p。{user_id,name,}')
public function getAll($limit = 500) {
$qb = $this->createQueryBuilder('u')
->select('u', 'd')
->leftJoin('p.userdata', 'd')
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()->getResult();
}