W ten sposób rozwiązałem Doktrynę „EntityManager jest zamknięty”. kwestia. Zasadniczo za każdym razem, gdy wystąpi wyjątek (np. Zduplikowany klucz) lub nie podanie danych dla obowiązkowej kolumny, Doctrine zamknie Entity Manager. Jeśli nadal chcesz współdziałać z bazą danych, musisz zresetować Entity Manger, wywołując resetManager()
metodę, o której wspomniał JGrinon .
W mojej aplikacji pracowałem z wieloma konsumentami RabbitMQ, którzy robili to samo: sprawdzanie, czy jednostka jest w bazie danych, jeśli tak, zwróć ją, jeśli nie, utwórz ją i zwróć. W ciągu kilku milisekund między sprawdzeniem, czy ta jednostka już istniała, a jej utworzeniem, inny konsument zrobił to samo i utworzył brakującą jednostkę, powodując, że drugi konsument poniósł zduplikowany klucz ( stan wyścigu ).
Doprowadziło to do problemu z projektowaniem oprogramowania. Zasadniczo próbowałem stworzyć wszystkie jednostki w jednej transakcji. Dla większości może to wydawać się naturalne, ale w moim przypadku było to zdecydowanie błędne koncepcyjnie. Rozważmy następujący problem: musiałem zapisać obiekt Football Match, który miał te zależności.
- grupa (np. Grupa A, Grupa B ...)
- runda (np. półfinały ...)
- miejsce (tj. stadion, na którym odbywa się mecz)
- status meczu (np. połowa, pełny etat)
- dwie drużyny grające w meczu
- sam mecz
Teraz, dlaczego utworzenie miejsca powinno odbywać się w tej samej transakcji co mecz? Możliwe, że właśnie otrzymałem nowe miejsce, którego nie ma w mojej bazie danych, więc muszę je najpierw utworzyć. Może się jednak zdarzyć, że to miejsce będzie gospodarzem innego dopasowania, więc inny konsument prawdopodobnie spróbuje również je utworzyć w tym samym czasie. Musiałem więc najpierw utworzyć wszystkie zależności w oddzielnych transakcjach, upewniając się, że resetowałem menedżera encji w zduplikowanym kluczu. Powiedziałbym, że wszystkie podmioty znajdujące się tam obok dopasowania można zdefiniować jako „współdzielone”, ponieważ potencjalnie mogłyby być częścią innych transakcji na innych konsumentach. Coś, co nie jest tam „udostępniane”, to samo dopasowanie, które prawdopodobnie nie zostanie utworzone przez dwóch konsumentów jednocześnie.
Wszystko to doprowadziło również do innego problemu. Jeśli zresetujesz Entity Manager, wszystkie obiekty, które odzyskałeś przed zresetowaniem, są dla Doctrine całkowicie nowe. Tak więc Doctrine nie będzie próbował uruchomić na nich AKTUALIZACJI, ale WSTAWIĆ ! Dlatego upewnij się, że utworzyłeś wszystkie zależności w logicznie poprawnych transakcjach, a następnie odzyskasz wszystkie obiekty z bazy danych przed ustawieniem ich na jednostkę docelową. Rozważmy następujący kod jako przykład:
$group = $this->createGroupIfDoesNotExist($groupData);
$match->setGroup($group);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
Więc myślę, że tak powinno być.
$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();
Mam nadzieję, że to pomoże :)