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

After I get a DBAL exception when inserting data, EntityManager closes and I'm not able to reconnect it.

I tried like this but it didn't get a connection.

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

Anyone an idea how to reconnect?

19
Why does the entity manager close?Jay Sheth
@JaySheth The entity manager could close after a DBAL exception, or if you're doing an EntityManager->clear() before a flush. I've seen some people using DBAL exceptions to branch the execution flow, and then ending up with an EntityManager closed error. If you're getting this error, there's something wrong in the execution flow in your program.ILikeTacos
@AlanChavez - I'm getting this error because I'm using Doctrine to write a semaphore flag to a table that is being accessed by multiple threads simultaneously. MySQL will error one of the two competing threads trying to create the semaphore, because the key constraint means only one of them can succeed. IMO there is a flaw in Doctrine that doesnt allow you to safely handle expected MySQL errors. Why should the entire MySQL connection be disconnected because one INSERT statement has a conflict?StampyCode
You'll also see this error if you are trying to log exceptions to a database in the app.exception_listener but the exception (such as a constraint violation) closed the connection.Lg102

19 Answers

27
votes

This is a very tricky problem since, at least for Symfony 2.0 and Doctrine 2.1, it is not possible in any way to reopen the EntityManager after it closes.

The only way I found to overcome this problem is to create your own DBAL Connection class, wrap the Doctrine one and provide exception handling (e.g. retrying several times before popping the exception out to the EntityManager). It is a bit hacky and I'm afraid it can cause some inconsistency in transactional environments (i.e. I'm not really sure of what happens if the failing query is in the middle of a transaction).

An example configuration to go for this way is:

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

The class should start more or less like this:

namespace Your\DBAL;

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

A very annoying thing is that you have to override each method of Connection providing your exception-handling wrapper. Using closures can ease some pain there.

79
votes

My solution.

Before doing anything check:

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

All entities will be saved. But it is handy for particular class or some cases. If you have some services with injected entitymanager, it still be closed.

37
votes

Symfony 2.0:

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

Symfony 2.1+:

$em = $this->getDoctrine()->resetManager();
32
votes

This is how I solved the Doctrine "The EntityManager is closed." issue. Basically each time there's an exception (i.e. duplicate key) or not providing data for a mandatory column will cause Doctrine to close the Entity Manager. If you still want to interact with the database you have to reset the Entity Manger by calling the resetManager() method as mentioned by JGrinon.

In my application I was running multiple RabbitMQ consumers that were all doing the same thing: checking if an entity was there in the database, if yes return it, if not create it and then return it. In the few milliseconds between checking if that entity already existed and creating it another consumer happened to do the same and created the missing entity making the other consumer incur in a duplicate key exception (race condition).

This led to a software design problem. Basically what I was trying to do was creating all the entities in one transaction. This may feel natural to most but was definitely conceptually wrong in my case. Consider the following problem: I had to store a football Match entity which had these dependencies.

  • a group (e.g. Group A, Group B...)
  • a round (e.g. Semi-finals...)
  • a venue (i.e. stadium where the match is taking place)
  • a match status (e.g. half time, full time)
  • the two teams playing the match
  • the match itself

Now, why the venue creation should be in the same transaction as the match? It could be that I've just received a new venue that it's not in my database so I have to create it first. But it could also be that that venue may host another match so another consumer will probably try to create it as well at the same time. So what I had to do was create all the dependencies first in separate transactions making sure I was resetting the entity manager in a duplicate key exception. I'd say that all the entities in there beside the match could be defined as "shared" because they could potentially be part of other transactions in other consumers. Something that is not "shared" in there is the match itself that won't likely be created by two consumers at the same time. So in the last transaction I expect to see just the match and the relation between the two teams and the match.

All of this also led to another issue. If you reset the Entity Manager, all the objects that you've retrieved before resetting are for Doctrine totally new. So Doctrine won't try to run an UPDATE on them but an INSERT! So make sure you create all your dependencies in logically correct transactions and then retrieve all your objects back from the database before setting them to the target entity. Consider the following code as an example:

$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 this is how I think it should be done.

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

I hope it helps :)

17
votes

You can reset your EM so

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

In Symfony 4.2+ you have to use the package:

composer require symfony/proxy-manager-bridge

otherwiese you get the exception:

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

Than you can reset the entityManager like this:

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

Symfony v4.1.6

Doctrine v2.9.0

Process inserting duplicates in a repository

  1. Get access a registry in your repo


    //begin of repo
    
    /** @var RegistryInterface */
    protected $registry;
    
    public function __construct(RegistryInterface $registry)
    {
        $this->registry = $registry;
        parent::__construct($registry, YourEntity::class);
    }

  1. Wrap risky code into transaction and reset manager in case of exception


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

4
votes

In controller.

Exception closes the Entity Manager. This makes troubles for bulk insert. To continue, need to redefine it.

/** 
* @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++;
    }               
  }                      
}
3
votes

I found an interesting article about this problem

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

Doctrine 2 Exception EntityManager is closed

2
votes

For what it's worth I found this issue was happening in a batch import command because of a try/catch loop catching an SQL error (with em->flush()) that I didn't do anything about. In my case it was because I was trying to insert a record with a non-nullable property left as null.

Typically this would cause a critical exception to happen and the command or controller to halt, but I was just logging this problem instead and carrying on. The SQL error had caused the entity manager to close.

Check your dev.log file for any silly SQL errors like this as it could be your fault. :)

0
votes

I had this issue. This how I fixed it.

The connection seems to close while trying to flush or persist. Trying to reopen it is a bad choice because creates new issues. I tryed to understand why the connection was closed and found that I was doing too many modifications before the persist.

persist() earlier solved the issue.

0
votes

I faced the same problem while testing the changes in Symfony 4.3.2

I lowered the log level to INFO

And ran the test again

And the logged showed this:

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

This means that some error in the code causes the:

Doctrine\ORM\ORMException: The EntityManager is closed.

So it is a good idea to check the log

0
votes

Same problem, solved with a simple code refactoring. The problem is sometime present when a required field is null, before do anithing, try to refactor your code. A better workflow can solve the problem.

0
votes

this how you reset the enitityManager in Symfony3. It should reopen the em if it has been closed:

In a Controller:

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

In a service:

  if (!$this->em->isOpen()) {
        $this->managerRegistry->resetManager('managername');
        $this->em = $this->managerRegistry->getManager('default');
    }

    $this->em->persist(...);

Don't forget to inject the '@doctrine' as a service argument in service.yml!

I'm wondering, if this problem happens if different methodes concurrently tries to access the same entity at the same time?

-1
votes

This is really old issue, but I just had similar problem. I was doing something like this :

// 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 was that clear detach all entities including the first one and throw error The EntityManager is closed.

In my case solution was to just do clear on distinct type of Entity and leave $entityOne still under EM:

$this->em->clear(SomeEntityClass::class);
-1
votes
The EntityManager is closed.

I had the same problem. The reason was missing column in table in database - i just had to run migration.

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

I faced the same problem. After looking at several places here is how I dealt with it.

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

Hope this helps someone!

-4
votes

I had the same error using Symfony 5 / Doctrine 2. One of my fields was named using a MySQL reserved word "order", causing a DBALException. When you want to use a reserved word, you have to escape it's name using back-ticks. In annotation form :

@ORM\Column(name="`order`", type="integer", nullable=false)