拥有side作为连接列的主键

时间:2012-03-24 02:49:37

标签: symfony doctrine-orm

注意:主题冗长但详细,如果您使用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。

此处描述的问题:

此处描述的建议解决方案:

所以有三种方法:

  1. 等待,看看是否在Doctrine中解决了提议的解决方案,并在将来的版本中修复(可能不会发生)
  2. 在每次查询中手动将UserData连接到User(仍然浪费资源,不需要UserData)
  3. 切换反面并将用户设为拥有方。
  4. 我决定选择#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,因为它们总是具有相同的值吗?您是否看到这种做事方式存在任何潜在问题?

    非常欢迎和赞赏有关此问题的任何其他提示或意见。

4 个答案:

答案 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所做的更多详情。

水合类型:HYDRATE_ARRAY

在我的情况下(当我不需要实体时)帮助的是指定我想要作为数组(而不是对象)获取。

$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();
    }