[Doctrine\ORM\ORMException]
The EntityManager is closed.
После того, как я получаю исключение DBAL при вставке данных, EntityManager закрывается, и я не могу его повторно подключить.
Я пробовал вот так, но связи не было.
$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();
Кто-нибудь знает, как переподключить?
app.exception_listener
но исключение (например, нарушение ограничения) закрыло соединение.Ответы:
Это очень сложная проблема, поскольку, по крайней мере для Symfony 2.0 и Doctrine 2.1, невозможно каким-либо образом повторно открыть EntityManager после его закрытия.
Единственный способ, который я нашел для решения этой проблемы, - это создать свой собственный класс DBAL Connection, обернуть Doctrine один и обеспечить обработку исключений (например, несколько раз повторить попытку перед тем, как передать исключение в EntityManager). Это немного взломано, и я боюсь, что это может вызвать некоторую несогласованность в транзакционных средах (т.е. я не совсем уверен в том, что произойдет, если неудачный запрос находится в середине транзакции).
Пример конфигурации для этого пути:
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
Класс должен начинаться примерно так:
namespace Your\DBAL; class ReopeningConnectionWrapper extends Doctrine\DBAL\Connection { // ... }
Очень раздражает то, что вам нужно переопределить каждый метод Connection, предоставляющий вашу оболочку обработки исключений. Использование закрытия может облегчить боль.
источник
Мое решение.
Прежде чем что-либо делать, проверьте:
if (!$this->entityManager->isOpen()) { $this->entityManager = $this->entityManager->create( $this->entityManager->getConnection(), $this->entityManager->getConfiguration() ); }
Все сущности будут сохранены. Но это удобно для определенного класса или некоторых случаев. Если у вас есть сервисы с внедренным entitymanager, он все равно будет закрыт.
источник
Symfony 2.0 :
$em = $this->getDoctrine()->resetEntityManager();
Symfony 2.1+ :
$em = $this->getDoctrine()->resetManager();
источник
resetEntityManager
устарело, начиная с Symfony 2.1. ИспользуйтеresetManager
вместоВот как я решил доктрину «EntityManager закрыт». вопрос. Обычно каждый раз, когда возникает исключение (например, дублированный ключ) или если не предоставляются данные для обязательного столбца, Doctrine закрывает Entity Manager. Если вы все еще хотите взаимодействовать с базой данных, вам необходимо сбросить Entity Manger, вызвав
resetManager()
метод, упомянутый JGrinon .В моем приложении я запускал несколько потребителей RabbitMQ, которые все делали одно и то же: проверяли, есть ли объект в базе данных, если да, верните его, если не создайте, а затем верните его. За несколько миллисекунд между проверкой существования этой сущности и ее созданием другой потребитель сделал то же самое и создал недостающую сущность, в результате чего другой потребитель столкнулся с дублирующимся ключевым исключением ( состояние гонки ).
Это привело к проблеме разработки программного обеспечения. По сути, я пытался создать все сущности в одной транзакции. Для большинства это может показаться естественным, но в моем случае это было определенно концептуально неверно. Рассмотрим следующую проблему: мне нужно было сохранить сущность футбольного матча, у которой были эти зависимости.
Итак, почему создание места проведения должно происходить в той же транзакции, что и матч? Возможно, я только что получил новое место, которого нет в моей базе данных, поэтому я должен сначала его создать. Но также может быть, что это место может провести еще один матч, поэтому другой потребитель, вероятно, попытается создать его в то же время. Поэтому мне нужно было сначала создать все зависимости в отдельных транзакциях, убедившись, что я сбрасываю диспетчер сущностей в исключении с дублированным ключом. Я бы сказал, что все сущности рядом с совпадением могут быть определены как «общие», потому что они потенциально могут быть частью других транзакций у других потребителей. То, что не является «общим», - это сам матч, который вряд ли будет создан двумя потребителями одновременно.
Все это также привело к другой проблеме. Если вы сбросите Entity Manager, все объекты, которые вы получили перед сбросом, будут совершенно новыми для Doctrine. Таким образом, Doctrine будет пытаться выполнить не ОБНОВЛЕНИЕ на них, а ВСТАВИТЬ ! Поэтому убедитесь, что вы создаете все свои зависимости в логически правильных транзакциях, а затем извлекаете все свои объекты обратно из базы данных, прежде чем устанавливать их в целевой объект. Рассмотрим в качестве примера следующий код:
$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. */
Так что, я думаю, это должно быть сделано.
$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();
Я надеюсь, что это помогает :)
источник
Вы можете сбросить свой EM так
// 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();
источник
В Symfony 4.2+ вы должны использовать пакет:
composer require symfony/proxy-manager-bridge
в противном случае вы получите исключение:
Resetting a non-lazy manager service is not supported. Declare the "doctrine.orm.default_entity_manager" service as lazy.
Затем вы можете сбросить entityManager следующим образом:
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(); } }
источник
В контроллере.
Исключение закрывает Entity Manager. Это создает проблемы для объемной вставки. Чтобы продолжить, нужно переопределить его.
/** * @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++; } } }
источник
Как бы то ни было, я обнаружил, что эта проблема возникает в команде пакетного импорта из-за того, что цикл try / catch улавливает ошибку SQL (с
em->flush()
), с которой я ничего не делал. В моем случае это произошло из-за того, что я пытался вставить запись с ненулевым свойством, оставленным как null.Обычно это приводило к возникновению критического исключения и остановке команды или контроллера, но вместо этого я просто регистрировал эту проблему и продолжал. Ошибка SQL привела к закрытию диспетчера сущностей.
Проверьте свой
dev.log
файл на наличие подобных глупых ошибок SQL, так как это может быть ваша ошибка. :)источник
Я нашел интересную статью об этой проблеме
if (!$entityManager->isOpen()) { $entityManager = $entityManager->create( $entityManager->getConnection(), $entityManager->getConfiguration()); }
Doctrine 2 Exception EntityManager закрыт
источник
Я столкнулся с той же проблемой при тестировании изменений в Symfony 4.3.2.
Я понизил уровень журнала до INFO
И снова запустил тест
И журнал показал это:
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?"} []
Это означает, что некоторая ошибка в коде вызывает:
Поэтому рекомендуется проверить журнал
источник
Symfony v4.1.6
Доктрина v2.9.0
Процесс вставки дубликатов в репозиторий
//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(); } }
источник
У меня была эта проблема. Вот как я это исправил.
Кажется, что соединение закрывается при попытке сброса или сохранения. Попытка открыть его заново - плохой выбор, потому что создает новые проблемы. Я попытался понять, почему соединение было закрыто, и обнаружил, что делал слишком много изменений, прежде чем они остались.
persist () ранее решал проблему.
источник
Это действительно старая проблема, но у меня была аналогичная проблема. Я делал что-то вроде этого:
// 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();
Проблема заключалась в том, что явное отсоединение всех сущностей, включая первый, и выдача ошибки EntityManager закрыто.
В моем случае решение заключалось в том, чтобы просто очистить отдельный тип Entity и оставить
$entityOne
все еще под EM:$this->em->clear(SomeEntityClass::class);
источник
Та же проблема, решенная простым рефакторингом кода. Проблема иногда возникает, когда обязательное поле имеет значение null, прежде чем выполнять что-либо, попробуйте реорганизовать код. Улучшенный рабочий процесс может решить проблему.
источник
У меня была такая же ошибка при использовании Symfony 5 / Doctrine 2. Одно из моих полей было названо с использованием зарезервированного в MySQL слова «порядок», что привело к исключению DBALException. Если вы хотите использовать зарезервированное слово, вы должны экранировать его имя с помощью обратных тиков. В форме аннотации:
@ORM\Column(name="`order`", type="integer", nullable=false)
источник
// 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();
источник
Я столкнулся с той же проблемой. Посмотрев несколько мест, вот как я с этим справился.
//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(); }
Надеюсь, это кому-то поможет!
источник