Подтвердить что ты не робот

Отношение "один-к-одному" отношения Doctrine2 автоматически к запросу

У меня есть запрос, который выглядит так:

Моя пользовательская сущность имеет отношение "один к одному", которое выглядит следующим образом:

/**
 * @var UserProfile
 *
 * @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user")
 */
private $userProfile;

В любое время, когда я делаю запрос для выбора нескольких пользовательских объектов, он создает дополнительный оператор выбора для каждого пользователя для запроса данных UserProfile, даже если я не получаю доступ к нему с помощью метода get. Мне не всегда нужны данные UserProfile, и я, конечно, не хочу загружать эти данные каждый раз, когда я показываю список пользователей.

Любая идея, почему эти запросы выполняются во время выполнения?

4b9b3361

Ответ 1

Вот объяснения в деталях:

https://groups.google.com/forum/#!topic/doctrine-user/fkIaKxifDqc

"выборка" в отображении - это подсказка, то есть, если это возможно Доктрина делает это, но если это невозможно, очевидно, это не так. Проксирование для ленивой загрузки просто не всегда возможно, технически. Ситуации, когда это невозможно:

1) взаимно-однозначный от обратной к обладающей стороне (появляется только в двунаправленные ассоциации "один-к-одному" ). Условие a) выше не может выполняться. 2) взаимно однозначное/многозначное соединение с иерархией и Целевой класс имеет подклассы (не является листом в иерархии классов). Условие b) выше не может быть выполнено.

В этих случаях проксирование технически невозможно.

Ваши варианты, чтобы избежать этой проблемы n + 1:

1) fetch-join через DQL: "выберите c, ca из Customer join c.cart ca". Один запрос, но соединение, однако, объединяется в одну ассоциацию относительно дешево.

2) принудительные частичные объекты. Нет дополнительных запросов, кроме также нет ленивой нагрузки: $query- > setHint (Query:: HINT_FORCE_PARTIAL_LOAD, верно)

3), если альтернативный формат результата (т.е. getArrayResult()) равен достаточных для использования, это также позволяет избежать этой проблемы.

У Бенджамина были некоторые идеи об автоматическом дозировании этих нагрузок избегайте n + 1 запросов, но это не меняет того факта, что проксирование не всегда возможно.

Ответ 2

Я потратил много времени на поиск решения. Для меня ни один из вариантов не был достаточно удовлетворительным, но, может быть, я смогу сэкономить некоторое время с помощью этого списка обходных решений:

1) Измените сторону владельца и обратную сторону http://developer.happyr.com/choose-owning-side-in-onetoone-relation - я не думаю, что это правильно с точки зрения дизайна БД каждый раз.

2) В таких функциях, как find, findAll и т.д., обратная сторона в OneToOne соединена автоматически (она всегда похожа на выборку EAGER). Но в DQL он не работает, как выборка EAGER, и это требует дополнительных запросов. Возможное решение - каждый раз присоединяться к обратному объекту

3) Если для некоторых прецедентов достаточно альтернативного формата результата (т.е. getArrayResult()), это также могло бы избежать этой проблемы.

4) Изменить обратную сторону, чтобы быть OneToMany - просто выглядит неправильно, возможно, может быть временным обходным решением.

5) Принудительные частичные объекты. Никаких дополнительных запросов, но и без ленивой загрузки: $query->setHint (Query::HINT_FORCE_PARTIAL_LOAD, true) - швы мне единственно возможное решение, но не без цены: Частичные объекты немного рискованны, потому что поведение вашего объекта не является нормальным. Например, если вы не указали в ->select() все ассоциации, которые вы будете иметь пользователь, вы можете иметь ошибку, потому что ваш объект не будет заполнен, все неконкретно выбранные ассоциации будут иметь значение null

6) Не отображает обратную двунаправленную ассоциацию OneToOne и либо использует явный сервис, либо более активный подход к записи - https://github.com/doctrine/doctrine2/pull/970#issuecomment-38383961 - И это похоже на Doctrine закрыл проблему

Ответ 3

Кажется, это открытый вопрос в Доктрине, см. Также

4.7.1. Почему дополнительный SQL-запрос выполняется каждый раз, когда я выбираю объект с отношением один-к-одному?

Если Doctrine обнаруживает, что вы выбираете обратную взаимно-однозначную ассоциацию, ему нужно выполнить дополнительный запрос для загрузки этого объекта, потому что он не может знать, нет ли такого объекта (установка null) или должен ли он установить прокси и какой идентификатор у этого прокси. Для решения этой проблемы в настоящее время необходимо выполнить запрос, чтобы узнать эту информацию.

Источник

Ответ 4

Как объяснил @apfelbox... теперь нет исправления.

Я пошел на решение OneToMany в сочетании с уникальным ключом:

User.php

/**
 * @ORM\OneToMany(targetEntity="TB\UserBundle\Entity\Settings", fetch="EXTRA_LAZY", mappedBy="user", cascade={"all"})
 */
protected $settings;

/**
 * @return \Doctrine\Common\Collections\Collection
 */
public function getSettings()
{
    return $this->settings;
}

и

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;

И для обеспечения уникальности в Settings.php:

use Doctrine\ORM\Mapping\UniqueConstraint;

И добавьте уникальный индекс

/**
 * @ORM\Entity
 * @ORM\Table(name="user_settings", uniqueConstraints={@UniqueConstraint(name="user", columns={"user_id"})})
 */
class Settings

Поэтому, когда я хочу получить доступ к пользовательским настройкам, мне просто нужно это (что будет запускать ТОЛЬКО ТОЛЬКО в этот конкретный момент)

$_settings = $user->getSettings()->current();

Я думаю, это самое чистое решение.

Ответ 5

Существует еще один вариант (который является лучшим IMHO) - вы можете использовать однонаправленный OneToOne.

В вашем случае - если вы редко используете UserProfile - установите ссылку в UserProfile

/**
 * @var User
 *
 * @ORM\OneToOne(targetEntity="User")
 */
private $user;

И просто не нарисуйте его в User. Вы можете загрузить его, когда он вам понадобится.

Если вы часто используете UserProfile, вы можете сделать его частью объекта User.

Ответ 6

В соответствии с reference вы можете добавить необязательный атрибут fetch

/**
 * @var UserProfile
 *
 * @ORM\OneToOne(targetEntity="UserProfile",mappedBy="user", fetch="LAZY")
 */
private $userProfile;