Ответ 1
Вы также можете заставить частичные объекты избавиться от ленивой загрузки:
use Doctrine\ORM\Query;
//...
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
ПРИМЕЧАНИЕ. Тема длинна, но детализирована и может пригодиться, если вы используете отношения Doctrine2 и oneToOne.
Недавно я столкнулся с проблемой в Доктрине:
Я создал объекты User и UserData с однонаправленным отношением oneToOne:
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, так как они всегда будут иметь одинаковое значение? Понимаете ли вы какие-либо потенциальные проблемы с этим способом выполнения вещей?
Любые другие советы или мнения по этому вопросу очень приветствуются и оцениваются.
Вы также можете заставить частичные объекты избавиться от ленивой загрузки:
use Doctrine\ORM\Query;
//...
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
Это известная проблема для ассоциаций OneToOne. Существует обсуждение github об этом, что стоит прочитать. Было предложено и отклонено решение (запрос тяги).
В вашем вопросе предлагается одно и то же решение, предложенное вкладчиками к Доктрине: измените собственную сторону отношений.
У меня была аналогичная проблема с объектом с именем Version
, у которого было OneToOne
двунаправленное отношение с Settings
. Каждый раз, когда я запрашивал Version
(скажем, для 10 записей конкретной версии), Doctrine будет делать дополнительные запросы для присоединенного Settings
(как если бы он был Lazy Загрузка этих объектов). Это произошло, хотя я не ссылался на Settings
в любом месте, например. $Version->getSettings()->getSomeProperty()
.
Единственное "решение" (hack), которое работает для меня, - это вручную включать JOIN для этого объекта Settings
каждый раз, когда я делал запрос на Version
. Но поскольку мне не нужна дополнительная сущность (в данном случае), это все равно будет лишним лишним запросом, каждый раз, когда я запрашиваю эту таблицу по-разному.
Основываясь на других предложениях, я подумал, что если я явно укажу это отношение как "лишнее ленивое", это сработает, например.
/**
* @ManyToMany(targetEntity="Settings", mappedBy="version", fetch="EXTRA_LAZY")
*/
Но это не влияет на гидратацию. Подробные сведения о том, что делает EXTRA_LAZY
, см. в Документах Doctrine.
Что помогло в моем случае (когда мне не нужна сущность), я должен был указать, что я хотел бы получить как массив (а не объект).
$query = $queryBuilder->getQuery();
$query->getResult(Query:HYDRATE_ARRAY);
Это возвращает массив, и в результате он не ленится загружать ассоциации OneToOne. Однако в других контекстах, где мне нужен объект entity, я должен был явно подключиться к сущности (несмотря на то, что не хотел этого).
Мое обходное решение состояло в том, чтобы превратить отношения 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.
Я столкнулся с этой же проблемой и помню, что в учебнике по символике был приведен пример того, как уменьшить ленивую загрузку, явно добавив левые соединения к таблицам, которые вам не нужны. Кажется странным включать таблицы в объединение, когда вы даже не хотите, чтобы эти данные вообще, но таким образом вы уменьшите все эти дополнительные запросы до 1, и он работает быстрее.
Поиск ленивой загрузки - около 1/5 пути вниз http://tutorial.symblog.co.uk/docs/customising-the-view-more-with-twig.html
Чтобы исправить это для проблемы user/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();
}