С Doctrine и Symfony в моем тестовом методе PHPUnit:
// Change username for user #1 (Sheriff Woody to Chuck Norris)
$form = $crawler->selectButton('Update')->form([
'user[username]' => 'Chuck Norris',
]);
$client->submit($form);
// Find user #1
$user = $em->getRepository(User::class)->find(1);
dump($user); // Username = "Sheriff Woody"
$user = $em->createQueryBuilder()
->from(User::class, 'user')
->andWhere('user.id = :userId')
->setParameter('userId', 1)
->select('
user
')
->getQuery()
->getOneOrNullResult()
;
dump($user); // Username = "Chuck Norris"
Почему два моих метода получения пользователя №1 возвращают разные результаты?
Пробовали ли вы двигаться ->select() до ->from()?




I предполагать* вы уже создали объект пользователя, который вы редактируете с помощью сканера, ранее в этой функции и проверили, что он там. Это приводит к тому, что он является управляемым объектом.
В природе данных заложено не синхронизироваться волшебным образом с базой данных, но должен быть некий автомат или какой-то метод для их синхронизации.
Метод find() всегда будет пытаться использовать кеш (если он явно не отключен, см. также примечание). Построитель запросов не будет, если вы явно вызовете Выполнение другого запроса может привести к тому, что кеш не попадет, что приведет к текущему результату. (хотя он должен обновить первый пользовательский объект...) [обновлено из-за комментария Арно Хильке]getResult() (или одну из его разновидностей), поскольку вы явно хотите, чтобы запрос был выполнен.
((( Примечание: Синхронизация объектов — это жесткий. В основном речь идет о согласованности в базе данных, но требуется все КИСЛОТА. Любой процесс, взаимодействующий с базой данных, должен предполагать, что он работает только с состоянием в момент свой первый запрос и является единственным пользователем базы данных. Если не должны быть соблюдены дополнительные ограничения и могут возникнуть несогласованные чтения, в этом случае уровни изоляции должны быть повышены (см. Также: транзакции или, точнее: изоляция). Таким образом, автоматическая синхронизация обычно не требуется.Доктрина использует определенные предположения для повышения производительности (в основном: изоляция/блокировка оптимистичны).Однако в вашем конкретном случае все эти вещи не имеют никакого значения... так как вы на самом деле хочу a неповторяемое чтение. )))
(* в противном случае поведение, которое вы видите, было бы В самом деле неожиданным)
Одним из простых решений было бы активное и явное синхронизировать данных из базы данных либо вызовом $em->refresh($user), либо — перед повторным получением пользователя — вызовом $em->clear(), что приведет к появлению объектов отделить все (очистка кеша, что может оказать заметное влияние на производительность) и позволяя вам снова вызывать find с правильными результатами.
Обратите внимание, что отсоединение сущностей означает, что любой объект, ранее возвращенный из диспетчера сущностей, должен быть отброшен и получен снова (не через обновление).
вместо проверки базы данных вы могли бы вместо этого сделать другой запрос к странице, которая отображает имя пользователя и проверяет, изменилось ли оно.
использование только одного диспетчера сущностей (то есть: совместное использование диспетчера сущностей/базы данных в модульном тесте с сервером по запросу) может быть разумным решением, но оно имеет свой собственный набор проблем. в основном пропущенные коммиты и сбросы могут избежать обнаружения.
используя один менеджер сущностей для настройки теста, поскольку сервер использует новый менеджер сущностей для выполнения своей работы, вы должны теоретически - чтобы сделать это на самом деле правильно - создать еще один менеджер сущностей для проверки поведения сервера.
комментарий: альтернативные решения 1, 2 и 3 будут работать с самым высоким уровнем изоляции, исходное решение, вероятно, не будет.
Неправильно, что построитель запросов не использует кеш. Я использовал тот же построитель запросов, что и в вопросе, и получаю кешированный результат.
@ArnoHilke, так ты говоришь, что не можешь воспроизвести проблему? это было бы еще хуже. однако вы, вероятно, в какой-то степени правы, потому что запросы построителя запросов можно кэшировать. Я просто предположил, что в данном конкретном случае это не так (иначе был бы такой же результат)
Да, я попытался воспроизвести проблему с моей собственной пользовательской сущностью и точными запросами, указанными в вопросе. В моем тестовом примере я обновляю пользователя и вызываю оба запроса. В этом тесте я получаю старый/кэшированный/неправильный результат для обоих. При вызове $em->clear() перед запросами я получаю правильный результат для обоих. Вот почему я попросил дополнительную информацию, потому что я не мог воспроизвести поведение, описанное в вопросе. Испытываете ли вы другое поведение (с построителем запросов, как описано в вопросе)? В этом случае это также может быть различием в версиях доктрины.
@ArnoHilke другой причиной может быть состояние гонки ... согласно symfony.com/doc/current/components/http_client.html, http-клиент является асинхронным, поэтому технически сервер может работать медленно, а диспетчер объектов теста может возвращаться быстрее. если тогда клиент и сервер используют один и тот же диспетчер сущностей (иногда это очень удобно), первый вызов диспетчера сущностей может вернуть старый объект, а кеш обновится, а второй запрос вернет новый объект, который был помещен туда из сервер ...
Я думаю, что стоит отметить разницу между «состояние/кэш в памяти диспетчера сущностей» и «Явное кэширование доктрины» в том, что только метод find будет извлекать данные из «состояние в памяти/кэш» с использованием «отображение личности», тогда как все варианты find, QueryBuilder, findBy будут извлекаться из явного кеша. . В результате, в случае отсутствия явного кеширования, я заметил, что варианты QueryBuilder и findBy всегда будут пытаться получить данные из базы данных.
Мне это кажется проблемой кэширования. Можете ли вы проверить это предположение, вызвав
$em->clearперед первым запросом, чтобы увидеть, приводит ли очистка кеша к возврату правильного результата? Кроме того, не могли бы вы предоставить более подробную информацию о вашей проблеме? Вы вызываете оба запроса в одном и том же тестовом примере? Не могли бы вы предоставить свои полные тестовые примеры и действия контроллера?