Ось як я розгадав доктрину "EntityManager закритий". проблема. В основному кожен раз, коли виникає виняток (тобто дублікат ключа) або ненадання даних для обов’язкового стовпця, Doctrine закриє Entity Manager. Якщо ви все одно бажаєте взаємодіяти з базою даних, вам слід скинути налаштування Entity Manger, викликавши resetManager()
метод, згаданий JGrinon .
У моїй програмі я запускав декількох споживачів RabbitMQ, які всі робили одне і те ж: перевіряли, чи є суть в базі даних, якщо так, повертати її, якщо не створювати, а потім повертати. Протягом декількох мілісекунд між перевіркою, чи вже існувала ця сутність, і створенням її іншим споживачем випадково зробили те саме, і створили відсутню сутність, змусивши іншого споживача спричинити виняток з повторенням ключа ( умова раси ).
Це призвело до проблеми з дизайном програмного забезпечення. В основному те, що я намагався зробити, це створити всі сутності в одній транзакції. Це може здаватися природним для більшості, але, безумовно, було концептуально помилковим у моєму випадку. Розглянемо таку проблему: мені довелося зберігати футбольний об’єкт матчу, який мав ці залежності.
- група (наприклад, група A, група B ...)
- раунд (наприклад, півфінал ...)
- місце проведення (тобто стадіон, на якому проводиться матч)
- статус матчу (напр., тайм)
- дві команди, що грають матч
- сам матч
Тепер, чому створення місця проведення має відбуватися в тій же транзакції, що і матч? Можливо, що я щойно отримав нове місце, якого немає у моїй базі даних, тому я маю його створити спочатку. Але також може бути, що на цьому майданчику може проходити інший матч, тому інший споживач, можливо, спробує створити його також одночасно. Отже, що мені потрібно було зробити, це створити всі залежності спочатку в окремих транзакціях, переконавшись, що я скидаю менеджер сутності у виняткові дублікати ключів. Я б сказав, що всі сутності, що знаходяться поруч із збігом, можна визначити як "спільні", оскільки вони потенційно можуть бути частиною інших транзакцій інших споживачів. Щось, що там не "ділиться", це сам матч, який, швидше за все, не буде створений двома споживачами одночасно.
Все це також призвело до іншого питання. Якщо ви скидаєте Entity Manager, усі об’єкти, які ви отримали перед скиданням, є для 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();
Сподіваюся, це допоможе :)