У меня есть 3 объекта, связанные таким образом:
Не волнуйтесь, я установил ассоциации, используя аннотации, но я думал, что следующий микс будет легче / чище, чтобы разоблачить мою проблему
Post @ORM\ManyToOne(targetEntity="User", fetch="EAGER") - author User @ORM\OneToOne(targetEntity="Vip", mappedBy="user", fetch="EAGER") - vip Vip # Notice that the primary key of vip is a foreign key on User primary @ORM\id @ORM\OneToOne(targetEntity="User", inversedBy="peliqan", fetch="EAGER") @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false) - user
Как вы можете видеть, все настроено на охоту.
Я хотел бы получить наборы сообщений вместе с и то и другое пользователей & Vip информация ТОЛЬКО, используя один запрос . (см. править)
Прямо сейчас, для каждой записи в записи я получаю один дополнительный запрос:
SELECT t0.valid AS valid_1, ... FROM vip t0 INNER JOIN user t10 ON t0.user_id = t10.id WHERE t0.user_id = ?
когда:
Я выполняю это
$results = $qb ->select('ps') ->leftJoin('ps.author','u')->addSelect('u') ->add('where', $qb->expr()->in('ps.id', $ids)) ->getQuery()->getResult();
и даже когда я пытаюсь использовать режим FETCH_EAGER, как это
->getQuery() ->setFetchMode('AppBundle\Entity\User', 'vip', ClassMetadata::FETCH_EAGER) ->getResult();
Заметка:
Мне удалось избавиться от дополнительного запроса путем enforcingQuery::HYDRATE_ARRAY
при getResult()
.
Запросы исчезли, что спасло треть первоначального времени.
Недостатком здесь является то, что при извлечении ассоциации в качестве массивов я больше не мог использовать Symfony\Component\Serializer\Annotation\Groups
для фильтрации свойств объектов и вынужден вручную редактировать набор результатов, чтобы удалить / преобразовать некоторые значения.
Уилт-ответ подходит для оригинального сообщения. Я не раскрыл свою проблему правильно. Я сказал, что хочу получить информацию Vip, потому что думал, что это хороший способ избавиться от дополнительного запроса, о котором я говорю выше. На самом деле мне не нужна информация Vip, но ->leftJoin('u.vip','v')->addSelect('v')
заставляет доктрину выдавать дополнительный запрос, который собирает информацию Vip! Есть ли способ предотвратить доктрину от выполнения этого запроса?
В Doctrine2 есть два типа запросов присоединения:
1) Регулярные объединения
2) Собирать соединения
Проверьте раздел 14.2.2 документации . Объединяется для более подробной информации.
Поэтому, если вы хотите получить join vips, вы должны addSelect
и leftJoin
их в своем запросе следующим образом:
$results = $qb ->select('ps') ->addSelect('u')->leftJoin('ps.author','u') ->addSelect('v')->leftJoin('u.vip','v') ->add('where', $qb->expr()->in('ps.id', $ids)) ->getQuery()->getResult();
Обновление после вашего комментария:
Я думал, что vip в результирующем наборе будет лучшим способом избавиться от дополнительного запроса
Вы не можете избавиться от дополнительного запроса, потому что вы не можете ленить загружать обратную сторону отношения «один к одному». См. Также этот пост для получения дополнительной информации:
Это ожидаемое поведение. Обратные стороны ассоциаций «один-к-одному» не могут быть ленивыми, технически. На обратной стороне нет внешнего ключа, поэтому невозможно решить, прокси-сервер или нет. Мы должны запросить связанный объект или присоединиться к нему. Обратите внимание, что это влияет только на обратные стороны однозначных ассоциаций, то есть на самом деле только обратную сторону двунаправленных взаимно однозначных ассоциаций.
Решением могло бы быть обратное отношение, так что пользователь становится владением отношения. В этом случае вы можете по крайней мере lazy-load Vip
внутри вашего объекта User
. Проблема с ленивой загрузкой переместилась бы на сторону Vip
, а это значит, что вы больше не сможете lazy-load
своего User
в Vip
.
В противном случае вы можете сделать запрос возвратом частичного объекта для предотвращения загрузки Vip
, но в целом вы должны быть очень осторожны с этим подходом.