The EntityManager is closed

SymfonyOrmDoctrine OrmEntitymanager

Symfony Problem Overview


[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?

Symfony Solutions


Solution 1 - Symfony

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.

Solution 2 - Symfony

Symfony 2.0:

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

Symfony 2.1+:

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

Solution 3 - Symfony

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 :)

Solution 4 - Symfony

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.

Solution 5 - Symfony

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

Solution 6 - Symfony

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

Solution 7 - Symfony

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


Solution 8 - Symfony

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++;
    }               
  }                      
}

Solution 9 - Symfony

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. :)

Solution 10 - Symfony

I found an interesting article about this problem

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

Doctrine 2 Exception EntityManager is closed

Solution 11 - Symfony

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.

Solution 12 - Symfony

I had the same problem when I tried to persist object with non-nullable column without setting default value for that property.

/**
 * @ORM\Column(type="boolean")
 */
protected $isActive;

When preparing that object in controller in some cases I do not explicitly set that property by:

$object->setIsActive(false);

So finally, even though that column in database have default value 0, Doctrine tried to save NULL in that column.

After changing to:

/**
 * @ORM\Column(type="boolean")
 */
protected $isActive = false;

problem disappeared.

Solution 13 - Symfony

In Symfony 5 / Doctrine ORM 2.10 the resetEntityManager method is not available.

What I found as a correct solution was to wrap persist and flush methods in a try, but using explicit transactions.

When an exception happens, I use resetManager method.

Here's an example:

try {
    $this->entityManager->beginTransaction();
    $this->entityManager->persist($entity);
    $this->entityManager->flush();
    $this->entityManager->commit();
} catch (Exception $e) {
    $this->entityManager->rollback();
    $this->managerRegistry->resetManager();
}

* managerRegistry refers to Doctrine\Persistence\ManagerRegistry

Source: This message at #orm channel in Doctrine' Slack.

Solution 14 - Symfony

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.

Solution 15 - Symfony

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

Solution 16 - Symfony

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?

Solution 17 - Symfony

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

Solution 18 - Symfony

The EntityManager is closed.

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

Solution 19 - Symfony

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

Solution 20 - Symfony

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!

Solution 21 - Symfony

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)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUeliView Question on Stackoverflow
Solution 1 - SymfonyGregsparrowView Answer on Stackoverflow
Solution 2 - SymfonyluisbgView Answer on Stackoverflow
Solution 3 - SymfonyFrancesco CasulaView Answer on Stackoverflow
Solution 4 - SymfonyAldo StracquadanioView Answer on Stackoverflow
Solution 5 - SymfonyJGrinonView Answer on Stackoverflow
Solution 6 - SymfonySebastian ViereckView Answer on Stackoverflow
Solution 7 - SymfonyAlexandr ShevchenkoView Answer on Stackoverflow
Solution 8 - SymfonyVadimView Answer on Stackoverflow
Solution 9 - SymfonyAdambeanView Answer on Stackoverflow
Solution 10 - Symfonystephan.madaView Answer on Stackoverflow
Solution 11 - SymfonyAxel BricheView Answer on Stackoverflow
Solution 12 - SymfonyBrodiView Answer on Stackoverflow
Solution 13 - SymfonyRicardo MartinsView Answer on Stackoverflow
Solution 14 - Symfonyuser3046563View Answer on Stackoverflow
Solution 15 - SymfonyBabak BandpeyView Answer on Stackoverflow
Solution 16 - SymfonyMoFaView Answer on Stackoverflow
Solution 17 - SymfonyNikola LoncarView Answer on Stackoverflow
Solution 18 - SymfonyArek PHPView Answer on Stackoverflow
Solution 19 - SymfonyEvgeny MalyshkinView Answer on Stackoverflow
Solution 20 - SymfonyMayank TiwariView Answer on Stackoverflow
Solution 21 - SymfonyMoonchildView Answer on Stackoverflow