Вот как я решил доктрину «EntityManager закрыт». вопрос. Обычно каждый раз, когда возникает исключение (например, дублированный ключ) или если не предоставляются данные для обязательного столбца, Doctrine закрывает Entity Manager. Если вы все еще хотите взаимодействовать с базой данных, вам необходимо сбросить Entity Manger, вызвав resetManager()
метод, упомянутый JGrinon .
В моем приложении я запускал несколько потребителей RabbitMQ, которые все делали одно и то же: проверяли, есть ли объект в базе данных, если да, верните его, если не создайте, а затем верните его. За несколько миллисекунд между проверкой существования этой сущности и ее созданием другой потребитель сделал то же самое и создал недостающую сущность, в результате чего другой потребитель столкнулся с дублирующимся ключевым исключением ( состояние гонки ).
Это привело к проблеме разработки программного обеспечения. По сути, я пытался создать все сущности в одной транзакции. Для большинства это может показаться естественным, но в моем случае это было определенно концептуально неверно. Рассмотрим следующую проблему: мне нужно было сохранить сущность футбольного матча, у которой были эти зависимости.
- группа (например, группа A, группа B ...)
- раунд (например, полуфинал ...)
- место проведения (например, стадион, на котором проходит матч)
- статус матча (например, перерыв, полный рабочий день)
- две команды играют в матче
- сам матч
Итак, почему создание места проведения должно происходить в той же транзакции, что и матч? Возможно, я только что получил новое место, которого нет в моей базе данных, поэтому я должен сначала его создать. Но также может быть, что это место может провести еще один матч, поэтому другой потребитель, вероятно, попытается создать его в то же время. Поэтому мне нужно было сначала создать все зависимости в отдельных транзакциях, убедившись, что я сбрасываю диспетчер сущностей в исключении с дублированным ключом. Я бы сказал, что все сущности рядом с совпадением могут быть определены как «общие», потому что они потенциально могут быть частью других транзакций у других потребителей. То, что не является «общим», - это сам матч, который вряд ли будет создан двумя потребителями одновременно.
Все это также привело к другой проблеме. Если вы сбросите Entity Manager, все объекты, которые вы получили перед сбросом, будут совершенно новыми для Doctrine. Таким образом, Doctrine будет пытаться выполнить не ОБНОВЛЕНИЕ на них, а ВСТАВИТЬ ! Поэтому убедитесь, что вы создаете все свои зависимости в логически правильных транзакциях, а затем извлекаете все свои объекты обратно из базы данных, прежде чем устанавливать их в целевой объект. Рассмотрим в качестве примера следующий код:
$group = $this->createGroupIfDoesNotExist($groupData);
$match->setGroup($group);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
Так что, я думаю, это должно быть сделано.
$group = $this->createGroupIfDoesNotExist($groupData);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
$group = $this->getGroup($groupData);
$venue = $this->getVenue($venueData);
$round = $this->getGroup($roundData);
$match->setGroup($group);
$match->setVenue($venue);
$match->setRound($round);
$matchTeamHome = new MatchTeam();
$matchTeamHome->setMatch($match);
$matchTeamHome->setTeam($teamHome);
$matchTeamAway = new MatchTeam();
$matchTeamAway->setMatch($match);
$matchTeamAway->setTeam($teamAway);
$match->addMatchTeam($matchTeamHome);
$match->addMatchTeam($matchTeamAway);
$em->persist($match);
$em->persist($matchTeamHome);
$em->persist($matchTeamAway);
$em->flush();
Я надеюсь, что это помогает :)