[Doctrine\ORM\ORMException]
The EntityManager is closed.
Po otrzymaniu wyjątku DBAL podczas wstawiania danych EntityManager zostaje zamknięty i nie mogę go ponownie połączyć.
Próbowałem w ten sposób, ale nie udało mi się uzyskać połączenia.
$this->em->close();
$this->set('doctrine.orm.entity_manager', null);
$this->set('doctrine.orm.default_entity_manager', null);
$this->get('doctrine')->resetEntityManager();
$this->em = $this->get('doctrine')->getEntityManager();
Czy ktoś ma pomysł, jak się ponownie połączyć?
app.exception_listener
ale wyjątek (na przykład naruszenie ograniczenia) zamknął połączenie.Odpowiedzi:
Jest to bardzo trudny problem, ponieważ przynajmniej w przypadku Symfony 2.0 i Doctrine 2.1 nie jest w żaden sposób możliwe ponowne otwarcie EntityManagera po jego zamknięciu.
Jedynym sposobem, w jaki znalazłem rozwiązanie tego problemu, jest utworzenie własnej klasy DBAL Connection, zawinięcie klasy Doctrine i zapewnienie obsługi wyjątków (np. Ponawianie kilkukrotnej próby przed wysłaniem wyjątku do EntityManager). Jest to trochę zepsute i obawiam się, że może powodować niespójność w środowiskach transakcyjnych (tj. Nie jestem do końca pewien, co się stanie, jeśli niepowodzenie zapytania jest w trakcie transakcji).
Przykładowa konfiguracja, którą można przejść w ten sposób:
doctrine: dbal: default_connection: default connections: default: driver: %database_driver% host: %database_host% user: %database_user% password: %database_password% charset: %database_charset% wrapper_class: Your\DBAL\ReopeningConnectionWrapper
Zajęcia powinny zaczynać się mniej więcej tak:
namespace Your\DBAL; class ReopeningConnectionWrapper extends Doctrine\DBAL\Connection { // ... }
Bardzo irytujące jest to, że musisz zastąpić każdą metodę połączenia, która zapewnia opakowanie do obsługi wyjątków. Używanie zamknięć może złagodzić ból.
źródło
Moje rozwiązanie.
Zanim cokolwiek zrobisz, sprawdź:
if (!$this->entityManager->isOpen()) { $this->entityManager = $this->entityManager->create( $this->entityManager->getConnection(), $this->entityManager->getConfiguration() ); }
Wszystkie podmioty zostaną zapisane. Ale jest to przydatne dla określonej klasy lub niektórych przypadków. Jeśli masz jakieś usługi z wstrzykniętym menadżerem uprawnień, nadal będą one zamknięte.
źródło
Symfony 2.0 :
$em = $this->getDoctrine()->resetEntityManager();
Symfony 2.1+ :
$em = $this->getDoctrine()->resetManager();
źródło
resetEntityManager
jest przestarzałe od Symfony 2.1. UżyjresetManager
zamiast tegoW 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.
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); // this is NOT OK! $venue = $this->createVenueIfDoesNotExist($venueData); $round = $this->createRoundIfDoesNotExist($roundData); /** * If the venue creation generates a duplicate key exception * we are forced to reset the entity manager in order to proceed * with the round creation and so we'll loose the group reference. * Meaning that Doctrine will try to persist the group as new even * if it's already there in the database. */
Więc myślę, że tak powinno być.
$group = $this->createGroupIfDoesNotExist($groupData); // first transaction, reset if duplicated $venue = $this->createVenueIfDoesNotExist($venueData); // second transaction, reset if duplicated $round = $this->createRoundIfDoesNotExist($roundData); // third transaction, reset if duplicated // we fetch all the entities back directly from the database $group = $this->getGroup($groupData); $venue = $this->getVenue($venueData); $round = $this->getGroup($roundData); // we finally set them now that no exceptions are going to happen $match->setGroup($group); $match->setVenue($venue); $match->setRound($round); // match and teams relation... $matchTeamHome = new MatchTeam(); $matchTeamHome->setMatch($match); $matchTeamHome->setTeam($teamHome); $matchTeamAway = new MatchTeam(); $matchTeamAway->setMatch($match); $matchTeamAway->setTeam($teamAway); $match->addMatchTeam($matchTeamHome); $match->addMatchTeam($matchTeamAway); // last transaction! $em->persist($match); $em->persist($matchTeamHome); $em->persist($matchTeamAway); $em->flush();
Mam nadzieję, że to pomoże :)
źródło
Możesz zresetować EM tak
// reset the EM and all aias $container = $this->container; $container->set('doctrine.orm.entity_manager', null); $container->set('doctrine.orm.default_entity_manager', null); // get a fresh EM $em = $this->getDoctrine()->getManager();
źródło
W Symfony 4.2+ musisz skorzystać z pakietu:
composer require symfony/proxy-manager-bridge
w przeciwnym razie otrzymasz wyjątek:
Resetting a non-lazy manager service is not supported. Declare the "doctrine.orm.default_entity_manager" service as lazy.
Następnie możesz zresetować entityManager w następujący sposób:
services.yaml:
App\Foo: - '@doctrine.orm.entity_manager' - '@doctrine'
Foo.php:
use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\DBAL\DBALException; use Doctrine\ORM\EntityManagerInterface; try { $this->entityManager->persist($entity); $this->entityManager->flush(); } catch (DBALException $e) { if (!$this->entityManager->isOpen()) { $this->entityManager = $this->doctrine->resetManager(); } }
źródło
W kontrolerze.
Wyjątek zamyka Entity Manager. To sprawia problemy przy wkładaniu zbiorczym. Aby kontynuować, musisz go przedefiniować.
/** * @var \Doctrine\ORM\EntityManager */ $em = $this->getDoctrine()->getManager(); foreach($to_insert AS $data) { if(!$em->isOpen()) { $this->getDoctrine()->resetManager(); $em = $this->getDoctrine()->getManager(); } $entity = new \Entity(); $entity->setUniqueNumber($data['number']); $em->persist($entity); try { $em->flush(); $counter++; } catch(\Doctrine\DBAL\DBALException $e) { if($e->getPrevious()->getCode() != '23000') { /** * if its not the error code for a duplicate key * value then rethrow the exception */ throw $e; } else { $duplication++; } } }
źródło
Znalazłem interesujący artykuł dotyczący tego problemu
if (!$entityManager->isOpen()) { $entityManager = $entityManager->create( $entityManager->getConnection(), $entityManager->getConfiguration()); }
EntityManager w Doctrine 2 Exception jest zamknięty
źródło
Co jest warte, zauważyłem, że ten problem występuje w poleceniu importu wsadowego z powodu pętli try / catch przechwytującej błąd SQL (z
em->flush()
), z którym nic nie zrobiłem. W moim przypadku było tak, ponieważ próbowałem wstawić rekord z właściwością niepodlegającą wartości null pozostawioną jako null.Zwykle powodowałoby to wystąpienie krytycznego wyjątku i zatrzymanie polecenia lub kontrolera, ale zamiast tego po prostu rejestrowałem ten problem i kontynuowałem. Błąd SQL spowodował zamknięcie menedżera encji.
Sprawdź, czy w
dev.log
pliku nie ma żadnych głupich błędów SQL, takich jak ten, ponieważ może to być Twoja wina. :)źródło
Napotkałem ten sam problem podczas testowania zmian w Symfony 4.3.2
Obniżyłem poziom dziennika do INFO
I ponownie uruchom test
A zalogowany pokazał to:
console.ERROR: Error thrown while running command "doctrine:schema:create". Message: "[Semantical Error] The annotation "@ORM\Id" in property App\Entity\Common::$id was never imported. Did you maybe forget to add a "use" statement for this annotation?" {"exception":"[object] (Doctrine\\Common\\Annotations\\AnnotationException(code: 0): [Semantical Error] The annotation \"@ORM\\Id\" in property App\\Entity\\Common::$id was never imported. Did you maybe forget to add a \"use\" statement for this annotation? at C:\\xampp\\htdocs\\dirty7s\\vendor\\doctrine\\annotations\\lib\\Doctrine\\Common\\Annotations\\AnnotationException.php:54)","command":"doctrine:schema:create","message":"[Semantical Error] The annotation \"@ORM\\Id\" in property App\\Entity\\Common::$id was never imported. Did you maybe forget to add a \"use\" statement for this annotation?"} []
Oznacza to, że jakiś błąd w kodzie powoduje:
Warto więc sprawdzić dziennik
źródło
Symfony v4.1.6
Doctrine v2.9.0
Proces wstawiania duplikatów do repozytorium
//begin of repo /** @var RegistryInterface */ protected $registry; public function __construct(RegistryInterface $registry) { $this->registry = $registry; parent::__construct($registry, YourEntity::class); }
//in repo method $em = $this->getEntityManager(); $em->beginTransaction(); try { $em->persist($yourEntityThatCanBeDuplicate); $em->flush(); $em->commit(); } catch (\Throwable $e) { //Rollback all nested transactions while ($em->getConnection()->getTransactionNestingLevel() > 0) { $em->rollback(); } //Reset the default em if (!$em->isOpen()) { $this->registry->resetManager(); } }
źródło
Miałem ten problem. Tak to naprawiłem.
Wydaje się, że połączenie zostaje zamknięte podczas próby opróżnienia lub utrwalenia. Próba ponownego otwarcia jest złym wyborem, ponieważ stwarza nowe problemy. Próbowałem zrozumieć, dlaczego połączenie zostało zamknięte i stwierdziłem, że robiłem zbyt wiele modyfikacji, zanim utrwaliłem się.
Persist () wcześniej rozwiązał problem.
źródło
To naprawdę stary problem, ale miałem podobny problem. Robiłem coś takiego:
// entity $entityOne = $this->em->find(Parent::class, 1); // do something on other entites (SomeEntityClass) $this->em->persist($entity); $this->em->flush(); $this->em->clear(); // and at end I was trying to save changes to first one by $this->em->persist($entityOne); $this->em->flush(); $this->em->clear();
Problem polegał na tym, że wyczyść odłącz wszystkie jednostki, w tym pierwszą, i zgłoś błąd . EntityManager jest zamknięty.
W moim przypadku rozwiązaniem było po prostu wyczyszczenie odrębnego typu podmiotu i pozostawienie
$entityOne
nadal pod EM:$this->em->clear(SomeEntityClass::class);
źródło
Ten sam problem, rozwiązany za pomocą prostej refaktoryzacji kodu. Problem pojawia się czasem, gdy wymagane pole ma wartość null, przed wykonaniem jakiejkolwiek operacji spróbuj zrefaktoryzować kod. Lepszy przepływ pracy może rozwiązać problem.
źródło
Miałem ten sam błąd podczas korzystania z Symfony 5 / Doctrine 2. Jedno z moich pól zostało nazwane przy użyciu zastrzeżonego słowa MySQL „order”, co spowodowało wyjątek DBALException. Jeśli chcesz użyć zastrzeżonego słowa, musisz uciec od jego nazwy za pomocą zwrotów. W formie adnotacji:
@ORM\Column(name="`order`", type="integer", nullable=false)
źródło
// first need to reset current manager $em->resetManager(); // and then get new $em = $this->getContainer()->get("doctrine"); // or in this way, depending of your environment: $em = $this->getDoctrine();
źródło
Miałem ten sam problem. Po obejrzeniu kilku miejsc, oto jak sobie z tym poradziłem.
//function in some model/utility function someFunction($em){ try{ //code which may throw exception and lead to closing of entity manager } catch(Exception $e){ //handle exception return false; } return true; } //in controller assuming entity manager is in $this->em $result = someFunction($this->em); if(!$result){ $this->getDoctrine()->resetEntityManager(); $this->em = $this->getDoctrine()->getManager(); }
Mam nadzieję, że to komuś pomoże!
źródło