it-swarm.com.de

Der EntityManager ist geschlossen

[Doctrine\ORM\ORMException]   
The EntityManager is closed.  

Nachdem ich beim Einfügen von Daten eine DBAL-Ausnahme erhalten habe, wird EntityManager geschlossen und ich kann die Verbindung nicht erneut herstellen. 

Ich habe es so versucht, aber es wurde keine Verbindung hergestellt.

$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();

Hat jemand eine Idee, wie man sich wieder verbindet?

58
Ueli

Dies ist ein sehr schwieriges Problem, da der EntityManager, zumindest für Symfony 2.0 und Doctrine 2.1, nach dem Schließen in keiner Weise wieder geöffnet werden kann.

Die einzige Möglichkeit, dieses Problem zu lösen, besteht darin, eine eigene DBAL-Verbindungsklasse zu erstellen, die Doctrine-Klasse einzuwickeln und die Ausnahmebehandlung bereitzustellen (z. B. mehrmals wiederholen, bevor die Ausnahme an den EntityManager ausgegeben wird). Es ist etwas hackig und ich befürchte, dass es in Transaktionsumgebungen zu Inkonsistenzen kommen kann (d. H. Ich bin mir nicht wirklich sicher, was passiert, wenn sich die fehlgeschlagene Abfrage mitten in einer Transaktion befindet).

Eine Beispielkonfiguration für diesen Weg lautet:

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

Die Klasse sollte mehr oder weniger so beginnen:

namespace Your\DBAL;

class ReopeningConnectionWrapper extends Doctrine\DBAL\Connection {
  // ...
}

Sehr ärgerlich ist, dass Sie jede Verbindungsmethode überschreiben müssen, die Ihren Wrapper zur Ausnahmebehandlung bereitstellt. Die Verwendung von Verschlüssen kann dort Schmerzen lindern.

23

Meine Lösung.

Bevor Sie etwas tun, überprüfen Sie bitte:

if (!$this->entityManager->isOpen()) {
    $this->entityManager = $this->entityManager->create(
        $this->entityManager->getConnection(),
        $this->entityManager->getConfiguration()
    );
}

Alle Objekte werden gespeichert. Aber es ist praktisch für bestimmte Klassen oder einige Fälle. Wenn Sie über Services mit injiziertem Entitymanager verfügen, wird dieser weiterhin geschlossen.

53
Gregsparrow

Symfony 2.0 :

$em = $this->getDoctrine()->resetEntityManager();

Symfony 2.1+ :

$em = $this->getDoctrine()->resetManager();
28
luisbg

Du kannst deine EM also zurücksetzen

// 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();
17
JGrinon

So löste ich die Lehre "Der EntityManager ist geschlossen." issue . Grundsätzlich wird bei jeder Ausnahme (d. h. doppelter Schlüssel) Doctrine den Entity Manager geschlossen. Wenn Sie weiterhin mit der Datenbank interagieren möchten, müssen Sie den Entity Manager zurücksetzen, indem Sie die resetManager()-Methode aufrufen, wie von JGrinon erwähnt.

In meiner Anwendung habe ich mehrere RabbitMQ-Konsumenten ausgeführt, die alle dasselbe machten: Prüfen, ob eine Entität in der Datenbank vorhanden war, wenn ja, zurückgeben, wenn nicht, und dann zurückgeben. In den wenigen Millisekunden zwischen den Prüfungen Wenn diese Entität bereits vorhanden war und sie erstellt wurde, tat dies ein anderer Konsument, und die fehlende Entität wurde erstellt, wodurch der andere Konsument eine doppelte Schlüsselausnahme erhielt.

Dies führte zu einem Software-Design-Problem. Im Grunde habe ich versucht, alle Entitäten in einer Transaktion zu erstellen. Dies mag für die meisten natürlich sein, war aber in meinem Fall definitiv falsch. Stellen Sie sich folgendes Problem vor: Ich musste eine Fußball-Entität speichern, die diese Abhängigkeiten hatte.

  • eine Gruppe (d. h. Gruppe A, Gruppe B ...)
  • eine Runde (d. h. Halbfinale)
  • ein Veranstaltungsort (d. h. Stadion, in dem das Spiel stattfindet)
  • ein Übereinstimmungsstatus (d. h. Halbzeit, Vollzeit)
  • die zwei Mannschaften, die das Spiel spielen
  • das Spiel selbst

Warum sollte die Veranstaltungsorterstellung in derselben Transaktion wie das Spiel sein? Es könnte sein, dass ich gerade einen neuen Veranstaltungsort erhalten habe, der nicht in meiner Datenbank vorhanden ist. Daher muss ich ihn zuerst erstellen. Es kann aber auch sein, dass der Veranstaltungsort möglicherweise ein anderes Spiel ausrichtet, so dass wahrscheinlich ein anderer Verbraucher versucht, es gleichzeitig zu erstellen. Ich musste also zunächst alle Abhängigkeiten in separaten Transaktionen erstellen und sicherstellen, dass ich den Entitätsmanager in einer doppelten Schlüsselausnahme zurücksetzte. Ich würde sagen, dass alle Entitäten neben dem Match als "gemeinsam genutzt" definiert werden könnten, da sie möglicherweise Teil anderer Transaktionen bei anderen Verbrauchern sein könnten. Etwas, das dort nicht "geteilt" wird, ist das Match selbst, das wahrscheinlich nicht von zwei Konsumenten zur gleichen Zeit erstellt wird. Ich erwarte also in der letzten Transaktion nur das Spiel und die Beziehung zwischen den beiden Teams und dem Spiel. All dies führte auch zu einem anderen Problem. Wenn Sie den Entity Manager zurücksetzen, sind alle Objekte, die Sie vor dem Zurücksetzen abgerufen haben, für Doctrine völlig neu. Doctrine versucht also nicht, ein UPDATE darauf auszuführen, sondern ein INSERT! Stellen Sie daher sicher, dass Sie alle Abhängigkeiten in logisch korrekten Transaktionen erstellen und alle Objekte aus der Datenbank abrufen, bevor Sie sie auf die Zieleinheit setzen. Betrachten Sie den folgenden Code als Beispiel:

$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.
 */

So denke ich, dass es getan werden sollte.

$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();

Ich hoffe, es hilft :)

17

Im Controller.

Ausnahme schließt den Entity Manager. Dies macht Probleme beim Masseneinsatz. Um fortzufahren, müssen Sie es neu definieren.

/** 
* @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++;
    }               
  }                      
}
4
Vadim

In Symfony 4.2 + müssen Sie das Paket verwenden:

composer require symfony/proxy-manager-bridge

ansonsten bekommst du die ausnahme:

Resetting a non-lazy manager service is not supported. Declare the "doctrine.orm.default_entity_manager" service as lazy.  

Dann können Sie den entityManager wie folgt zurücksetzen:

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();
    }
}
3

Ich fand dieses Problem in einem Batch-Importbefehl aufgrund einer Try/Catch-Schleife, die einen SQL-Fehler (mit em->flush()) abfängt, über den ich nichts unternommen habe. In meinem Fall lag es daran, dass ich versuchte, einen Datensatz mit einer nicht-nullbaren Eigenschaft als Null einzufügen.

Normalerweise würde dies zu einer kritischen Ausnahme und zum Anhalten des Befehls oder Controllers führen, aber stattdessen protokollierte ich dieses Problem und fuhr fort. Der SQL-Fehler hatte dazu geführt, dass der Entitätsmanager geschlossen wurde.

Überprüfen Sie Ihre dev.log-Datei auf dumme SQL-Fehler wie diese, da dies möglicherweise Ihre Schuld ist. :)

1
Adambean

Versuchen Sie es mit:

$em->getConnection()->[setNestTransactionsWithSavepoints][1](true);

bevor Sie eine Transaktion starten.

Bei der Connection::rollback-Methode wird nach nestTransactionsWithSavepoints property gesucht.

1
zechim

Das ist wirklich ein altes Problem, aber ich hatte gerade ein ähnliches Problem. Ich habe so etwas gemacht:

// 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();

Das Problem war, dass alle Entitäten, einschließlich der ersten, entfernt wurden und ein Fehler aufgetreten ist Der EntityManager ist geschlossen.

In meinem Fall Lösung war es, nur einen bestimmten Entitätstyp zu definieren und $entityOne Noch unter EM zu belassen:

$this->em->clear(SomeEntityClass::class);
0
Nikola Loncar

Symfony v4.1.6

Lehre v2.9.0

Einfügen von Duplikaten in ein Repository

  1. Erhalten Sie Zugriff auf eine Registrierung in Ihrem Repo

//begin of repo

/** @var RegistryInterface */
protected $registry;

public function __construct(RegistryInterface $registry)
{
    $this->registry = $registry;
    parent::__construct($registry, YourEntity::class);
}

</ pre> </ code>

  1. Wickeln Sie in Ausnahmefällen riskanten Code in den Transaktions- und Rested-Manager ein

//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();
    }
}

</ pre> </ code>

0

Ich hatte das gleiche Problem beim Testen der Änderungen in Symfony 4.3.2

Ich habe den Log-Level auf INFO gesenkt

Und führte den Test erneut aus

Und der geloggte zeigte dies:

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?"} []

Dies bedeutet, dass ein Fehler im Code Folgendes verursacht:

Doctrine\ORM\ORMException: The EntityManager is closed.

Es ist daher eine gute Idee, das Protokoll zu überprüfen

0
Babak Bandpey

Ich hatte dieses Problem. So habe ich es behoben.

Die Verbindung scheint während des Flush-Vorgangs geschlossen zu sein. Der Versuch, es erneut zu öffnen, ist eine schlechte Wahl, da neue Probleme entstehen. Ich versuchte zu verstehen, warum die Verbindung geschlossen wurde, und stellte fest, dass ich vor dem Fortbestehen zu viele Änderungen vorgenommen hatte. 

persist () löste das Problem früher.

0
user3046563

Ich habe einen interessanten Artikel zu diesem Problem gefunden

if (!$entityManager->isOpen()) {
  $entityManager = $entityManager->create(
    $entityManager->getConnection(), $entityManager->getConfiguration());
}

Doctrine 2 Ausnahme EntityManager ist geschlossen

0
stephan.mada