20

我很难为此找到正确的结果。

我有一对一的映射。有两个表:

/**
* @ORM\Table(name="users")
* @ORM\Entity
*/
class Users {

   /**
    * @ORM\OneToOne(targetEntity="UsersSettings", mappedBy="user",cascade={"persist"})
   */
   private $userSetting;

   //getters and setters
}

/**
* @ORM\Table(name="notifications_settings")
* @ORM\Entity
*/
class UsersSettings {

   /**
   * @var Users
   *
   * @ORM\OneToOne(targetEntity="Users", inversedBy="userSetting")
   * @ORM\JoinColumns({
   *   @ORM\JoinColumn(name="user_id", referencedColumnName="id")
   * })
   */
   private $user;
}

每当我获取如下实体之一时:

$q = $this
            ->createQueryBuilder('u')
            ->select('u, r')
            ->leftJoin('u.roles', 'r')
            ->where('u.username = :username OR u.email = :email')
            ->setParameter('username', $username)
            ->setParameter('email', $username)
            ->getQuery();

Doctrine 立即加入我不想要的 usersSettings 实体:

SELECT t0.id AS id1, t0.username AS username2, t0.email AS email3, t0.password AS password4, t29.id AS id30, t29.is_notify_by_email AS is_notify_by_email31, t29.user_id AS user_id32 FROM users t0 LEFT JOIN users_settings t29 ON t29.user_id = t0.id 在哪里 t0.id = ?

其他类型的映射喜欢OneToManyManyToOne执行延迟加载,但在一对一映射的情况下,我无法配置为延迟加载。我怎样才能延迟加载这种关系?我正在使用学说 2.3 和 Symfony 2.1

4

5 回答 5

17

使用提示 HINT_FORCE_PARTIAL_LOAD 来避免延迟加载。

...
$qb->getQuery()->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
于 2013-05-05T00:06:46.203 回答
8

一对一关联的反面不能偷懒。

解释:

这是预期的行为。反面没有外键,因此无法决定是否代理它。我们必须查询关联的对象或加入它。请注意,这只影响单值关联的反面,也就是说,实际上只影响双向一对一关联的反面。

为什么我们不能在没有 FK 的情况下创建代理?

这很简单,在单面为反面的一对多关联中,它包含一个集合。集合可能是空的,也可能不是,但总有一个集合,所以总是把一个代理集合放在那里很容易和正确。

如果你有一个单值的一面是反面,你怎么能决定是否放一个代理对象呢?如果没有看到外键,您将无法区分:没有关联对象(因此将代理对象放置在适当的位置将完全是错误的)或存在一个并且它是哪种类型,如果涉及继承(因为放置错误类型的代理也是错误的)。

请参阅https://github.com/doctrine/orm/issues/4389上的讨论

于 2019-05-29T14:13:08.007 回答
0

解决方案 1:您可以更改关系的拥有方和反方

解决方案 2:您可以将反面的映射声明为 oneToMany 而不是 oneToOne,然后在 setter(如果需要 setter)中将旧集合替换为仅包含一个元素的新集合,并在 getter 中返回集合的第一个元素(即唯一的元素),像这样:

class Users {

    /**
    * 
    @ORM\OneToMany(targetEntity="UsersSettings", mappedBy="user")
    */
    private $userSetting;
   
    public function setUserSettings(UserSettings $setting): void
    {
        $setting->setUser($this);
        $this->userSettings = new ArrayCollection([$setting]);   
    }

    public function getUserSettings(): UserSettings
    {
        return $this->userSettings->first();
    }
}

在这种情况下,UserSettings 实体保持不变

于 2021-09-19T09:09:44.417 回答
0

这个问题至少从 2010 年 2 月 21 日就已经知道了 - https://github.com/doctrine/orm/issues/4389。我们有 7 种解决方法,前 6 种在这里描述 - https://stackoverflow.com/a/34353840/5735549

最流行的解决方案的缺点:

  1. 更改关系的拥有方和反方”(https://developer.happyr.com/choose-owning-side-in-onetoone-relation) - 将在拥有方产生额外的 id。例如,用户表将包含不必要的列,如 user_setting_id、user_cart_id、user_notification_settings_id 等。如果您决定对 user 和 user_settings 表使用相同的 id 策略,它看起来会更加错误。users 表中的行将类似于“id=123, user_setting_id=123, user_cart_id=123, ...=123”。

  2. 将反面更改为 OneToMany 而不是 OneToOne ” - 如果您以这种方式执行原则架构验证器(控制台原则:架构:验证)将产生错误,如“如果关联 UserSettings#user 是一对一的,那么反面用户#userSettings 也必须是一对一的”,symfony profiler 会在每个请求的“教义/实体映射”部分显示这个错误,它让你对错误有更大的容忍度并学会忽略它们。

具有延迟加载支持的双向 OneToOne 关联的另一个解决方案是“ OneToMany/ManyToOne 技巧” - 允许您延迟加载相关实体,将 id 存储在预期的一侧并且没有架构验证错误。

以下是具有相同 id 策略的 user 和 userSetting 的示例:

class User
{
    #[ORM\OneToMany(mappedBy: 'user', targetEntity: UserSettings::class, cascade: ['persist', 'remove'], fetch: 'LAZY', orphanRemoval: true)]
    #[ORM\JoinColumn(name: 'id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    private Collection $userSettings;

    public function __construct()
    {
        $this->userSettings = new ArrayCollection;
    }    
    
    public function setUserSettings(UserSettings $setting): void
    {
        $this->userSettings = new ArrayCollection([$setting]);
    }

    public function getUserSettings(): ?UserSettings
    {
        return $this->userSettings->isEmpty() ? null : $this->userSettings->first();
    }
}

class UserSettings
{
    #[ORM\Id]
    #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'userSettings')]
    #[ORM\JoinColumn(name: 'id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    private User $user;

    public function __construct(User $user)
    {
        $this->user = $user;
        $this->user->setUserSettings($this);
    }

    public function getUser(): User
    {
        $this->user->setUserSettings($this);

        return $this->user;
    }
}

于 2021-10-26T13:14:50.770 回答
-3

您可以在注释中启用关联的额外延迟加载。fetch="EXTRA_LAZY"

* @ORM\OneToOne(targetEntity="Users", inversedBy="userSetting", fetch="EXTRA_LAZY")
于 2013-01-21T03:00:28.453 回答