[Doctrine\ORM\ORMException]
The EntityManager is closed.
Después de recibir una excepción DBAL al insertar datos, EntityManager se cierra y no puedo volver a conectarlo.
Intenté así pero no conseguí una conexión.
$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();
¿Alguien tiene una idea de cómo volver a conectar?
app.exception_listener
pero la excepción (como una infracción de restricción) cerró la conexión.Respuestas:
Este es un problema muy complicado ya que, al menos para Symfony 2.0 y Doctrine 2.1, no es posible de ninguna manera volver a abrir EntityManager después de que se cierre.
La única forma que encontré para superar este problema es crear su propia clase de conexión DBAL, envolver la de Doctrine y proporcionar manejo de excepciones (por ejemplo, volver a intentarlo varias veces antes de enviar la excepción al EntityManager). Es un poco hacky y me temo que puede causar alguna inconsistencia en los entornos transaccionales (es decir, no estoy realmente seguro de lo que sucede si la consulta fallida está en medio de una transacción).
Una configuración de ejemplo para seguir de esta manera es:
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
La clase debería comenzar más o menos así:
namespace Your\DBAL; class ReopeningConnectionWrapper extends Doctrine\DBAL\Connection { // ... }
Algo muy molesto es que debe anular cada método de conexión que proporciona su contenedor de manejo de excepciones. El uso de cierres puede aliviar un poco el dolor allí.
fuente
Mi solución.
Antes de hacer nada, compruebe:
if (!$this->entityManager->isOpen()) { $this->entityManager = $this->entityManager->create( $this->entityManager->getConnection(), $this->entityManager->getConfiguration() ); }
Todas las entidades se guardarán. Pero es útil para una clase particular o algunos casos. Si tiene algunos servicios con entitymanager inyectado, aún estará cerrado.
fuente
Symfony 2.0 :
$em = $this->getDoctrine()->resetEntityManager();
Symfony 2.1 o superior :
$em = $this->getDoctrine()->resetManager();
fuente
resetEntityManager
está en desuso desde Symfony 2.1. Úselo en suresetManager
lugarAsí es como resolví la Doctrina "El EntityManager está cerrado". problema. Básicamente, cada vez que hay una excepción (es decir, una clave duplicada) o no proporcionar datos para una columna obligatoria, Doctrine cerrará Entity Manager. Si aún desea interactuar con la base de datos, debe restablecer el Administrador
resetManager()
de entidades llamando al método mencionado por JGrinon .En mi aplicación, estaba ejecutando varios consumidores RabbitMQ que estaban haciendo lo mismo: verificar si una entidad estaba allí en la base de datos, si es así, devolverla, si no, crearla y luego devolverla. En los pocos milisegundos entre verificar si esa entidad ya existía y crearla, otro consumidor hizo lo mismo y creó la entidad faltante, lo que hizo que el otro consumidor incurriera en una excepción de clave duplicada ( condición de carrera ).
Esto llevó a un problema de diseño de software. Básicamente, lo que estaba tratando de hacer era crear todas las entidades en una transacción. Esto puede parecer natural para la mayoría, pero definitivamente fue conceptualmente incorrecto en mi caso. Considere el siguiente problema: tuve que almacenar una entidad de partido de fútbol que tenía estas dependencias.
Ahora bien, ¿por qué la creación del lugar debe realizarse en la misma transacción que el partido? Podría ser que acabo de recibir un nuevo lugar que no está en mi base de datos, así que primero tengo que crearlo. Pero también podría ser que ese lugar pueda albergar otro partido, por lo que es probable que otro consumidor intente crearlo al mismo tiempo. Entonces, lo que tuve que hacer fue crear todas las dependencias primero en transacciones separadas asegurándome de restablecer el administrador de entidades en una excepción de clave duplicada. Yo diría que todas las entidades que se encuentran al lado de la coincidencia podrían definirse como "compartidas" porque podrían ser parte de otras transacciones en otros consumidores. Algo que no se "comparte" allí es la coincidencia en sí que probablemente no será creada por dos consumidores al mismo tiempo.
Todo esto también llevó a otro problema. Si restableces Entity Manager, todos los objetos que has recuperado antes de restablecer son para Doctrine totalmente nuevos. ¡Entonces Doctrine no intentará ejecutar una ACTUALIZACIÓN en ellos, sino un INSERT ! Así que asegúrese de crear todas sus dependencias en transacciones lógicamente correctas y luego recupere todos sus objetos de la base de datos antes de configurarlos en la entidad de destino. Considere el siguiente código como ejemplo:
$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. */
Así es como creo que debería hacerse.
$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();
Espero que ayude :)
fuente
Puede restablecer su EM para
// 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();
fuente
En Symfony 4.2+ tienes que usar el paquete:
composer require symfony/proxy-manager-bridge
de lo contrario, obtienes la excepción:
Resetting a non-lazy manager service is not supported. Declare the "doctrine.orm.default_entity_manager" service as lazy.
De lo que puede restablecer el entityManager de esta manera:
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(); } }
fuente
En controlador.
La excepción cierra el Entity Manager. Esto crea problemas para el inserto a granel. Para continuar, es necesario redefinirlo.
/** * @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++; } } }
fuente
Encontré un artículo interesante sobre este problema.
if (!$entityManager->isOpen()) { $entityManager = $entityManager->create( $entityManager->getConnection(), $entityManager->getConfiguration()); }
EntityManager de excepción de Doctrine 2 está cerrado
fuente
Por lo que vale, encontré que este problema estaba sucediendo en un comando de importación por lotes debido a un bucle try / catch que detecta un error SQL (con
em->flush()
) sobre el que no hice nada. En mi caso, fue porque estaba tratando de insertar un registro con una propiedad no anulable dejada como nula.Por lo general, esto causaría que ocurriera una excepción crítica y que el comando o controlador se detuviera, pero solo estaba registrando este problema y continuaba. El error de SQL había provocado el cierre del administrador de entidades.
Verifique su
dev.log
archivo en busca de errores de SQL tontos como este, ya que podría ser su culpa. :)fuente
Me enfrenté al mismo problema mientras probaba los cambios en Symfony 4.3.2
Bajé el nivel de registro a INFO
Y corrí la prueba de nuevo
Y el registrado mostró esto:
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?"} []
Esto significa que algún error en el código provoca:
Por tanto, es una buena idea comprobar el registro
fuente
Symfony v4.1.6
Doctrina v2.9.0
Proceso de inserción de duplicados en un repositorio
//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(); } }
fuente
Tuve este problema. Así es como lo arreglé.
La conexión parece cerrarse al intentar vaciar o persistir. Intentar reabrirlo es una mala elección porque crea nuevos problemas. Intenté entender por qué se cerró la conexión y descubrí que estaba haciendo demasiadas modificaciones antes de persistir.
persist () anteriormente resolvió el problema.
fuente
Intente usar:
$em->getConnection()->[setNestTransactionsWithSavepoints][1](true);
antes de iniciar una transacción.
En el
Connection::rollback
método, comprueba lanestTransactionsWithSavepoints
propiedad.fuente
Este es un problema realmente antiguo, pero tuve un problema similar. Estaba haciendo algo como esto:
// 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();
El problema fue que despejar todas las entidades, incluida la primera, y arrojar el error El EntityManager está cerrado.
En mi caso, la solución fue simplemente aclarar un tipo distinto de entidad y dejarlo
$entityOne
todavía en EM:$this->em->clear(SomeEntityClass::class);
fuente
Mismo problema, resuelto con una simple refactorización de código. El problema está presente en algún momento cuando un campo obligatorio es nulo, antes de hacer nada, intente refactorizar su código. Un mejor flujo de trabajo puede resolver el problema.
fuente
Tuve el mismo error al usar Symfony 5 / Doctrine 2. Uno de mis campos fue nombrado usando una palabra reservada de MySQL "orden", lo que provocó una DBALException. Cuando desee usar una palabra reservada, debe escapar de su nombre usando ticks inversos. En forma de anotación:
@ORM\Column(name="`order`", type="integer", nullable=false)
fuente
// 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();
fuente
Me enfrenté al mismo problema. Después de mirar varios lugares, aquí es cómo lo manejé.
//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(); }
¡Espero que esto ayude a alguien!
fuente