Doctrine: A new entity was found through the relationship странное поведение
Видел что на stackoverflow много подобных вопросов, но не видел ни одного рабочего решения. Ситуации возникновения у всех разные, тем не менее приведу свою проблему.
У меня работает парсер XML фида, большого, выглядит так:
public function run(): void
{
$this->runParameters->entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
$filePath = '/tmp/guzzle-downloadGLOgDf';
//$filePath = $this->downloader->downloadXMLFeed($this->runParameters->feedLink);
$reader = new FeedReader($filePath);
$countOffers = 0;
foreach ($reader->read() as $item) {
if (!$this->runParameters->entityManager->isOpen()) {
$this->runParameters->entityManager = $this->runParameters->entityManager->create(
$this->runParameters->entityManager->getConnection(),
$this->runParameters->entityManager->getConfiguration()
);
}
//$this->runParameters->entityManager->beginTransaction();
try {
$parser = new Parser\Wrapper($item, $this->runParameters);
$countOffers += $parser->parse();
//$this->runParameters->entityManager->commit();
} catch (\Throwable $t) {
//$this->runParameters->entityManager->rollback();
$this->runParameters->logger->error('Failed parsed offer', [
'code' => $t->getCode(),
'message' => $t->getMessage(),
'file' => $t->getFile(),
'line' => $t->getLine(),
'trace' => $t->getTraceAsString(),
]);
}
$this->runParameters->entityManager->clear();
//$this->runParameters->entityManager->close();
}
$this->runParameters->logger->info('Handled ' . $countOffers . ' offers');
}
Из кода видно, что я пытался использовать решения с пересозанием соединения, с его закрытием, с его очисткой и множество других способов, написанных здесь на stackoverflow.
Однако, ошибка все равно возникает, не смотря на то, что все зависимые сущности $this->em->persist($entity). Я думал проблема в транзакции, закомментировал транзакцию, проблема не в ней. Проходит несколько циклов, распарсивается свободно несколько итераций, но как только выпадает какая либо ошибка БД, хоть по нарушению уникальности, хоть еще какая-либо, сразу после нее начинает сыпаться:
Multiple non-persisted new entities were found through the given association graph:
* A new entity was found through the relationship 'ENTITY_NAME' that was not configured to cascade persist operations for entity:
* ...
Сущности разные, по сути она падает на все зависимые сущности, участвующие в парсинге. Повторяю везде сделан persist и даже flush сущности, которые создавались или изменялись. Да после изменения они могли быть переданы в другую сущность. и это нормально. И самое главное всё это работает, пока не выскочит хоть какая-то ошибка БД. Хочется разобраться почему это вообще происходит
Ответы (1 шт):
Ушло не мало времени чтобы найти более менее нормальный способ.
Как и писал в вопросе:
if (!$this->runParameters->entityManager->isOpen()) {
$this->runParameters->entityManager = $this->runParameters->entityManager->create(
$this->runParameters->entityManager->getConnection(),
$this->runParameters->entityManager->getConfiguration()
);
}
Этот способ восстановления соединения не работает и только пораждает проблемы с зависимостями. Не знаю почему все пишут использовать его, может на простых сущностях и все нормально, но на сущностях с большим числом зависимостей этот способ не работает.
А решение следующее! Если это команда, как в моем случае, то через $this->getApplication()->getKernel()->getContainer()->get('doctrine'); можно запросить контейнер доктрины. Этот контейнер я записал тоже в параметры запуска, это не красиво, но это лучше чем передавать его через цепочку методов. Далее там где был отлов ошибки, где соединение завершает работу я сделал:
try {
$parser = new Parser\Wrapper($item, $this->runParameters);
$countOffers += $parser->parse();
$this->runParameters->entityManager->commit();
} catch (\Throwable $t) {
$this->runParameters->entityManager->rollback();
$this->runParameters->logger->error('Failed parsed offer', [
'code' => $t->getCode(),
'message' => $t->getMessage(),
'file' => $t->getFile(),
'line' => $t->getLine(),
'trace' => $t->getTraceAsString(),
]);
$this->runParameters->entityManager->clear(); //Чистить не обязательно, оставил в качестве эксперимента
$this->runParameters->entityManager->close(); //Закрываем соединение, если вдруг там еще что-то случилось оно вдруг не закрылось.
$this->runParameters->doctrine->resetManager(); //Делаем reset из того самого контейнера
}
В таком случае все последующие итерации начинают работать даже после падения и вылета ошибки из БД, и при этом не нарушаются зависимости.
Решение мне не очень нравится, но мне и не нравится то, что доктрина закрывает соединение после ошибки из бд. И в модели запрос-ответ это отличное решение, но для парсинга чужеродных сущностей всех ошибок не отловишь, поэтому просто необходимо продолжать парсинг, а "кривые" сущности просто пропускать и вести лог о них.