EntityManager已关闭

时间:2013-01-10 13:03:55

标签: symfony orm doctrine-orm entitymanager

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

任何人都知道如何重新连接?

20 个答案:

答案 0 :(得分:53)

我的解决方案。

在做任何事情之前检查:

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

将保存所有实体。但它对特定类或某些情况很方便。如果你有一些注入了entitymanager的服务,它仍然会被关闭。

答案 1 :(得分:31)

Symfony 2.0

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

Symfony 2.1 +

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

答案 2 :(得分:24)

这是一个非常棘手的问题,因为至少对于Symfony 2.0和Doctrine 2.1来说,在关闭后不可能以任何方式重新打开EntityManager。

我发现克服此问题的唯一方法是创建自己的DBAL Connection类,包装Doctrine并提供异常处理(例如,在将异常弹出到EntityManager之前重试几次)。它有点hacky,我担心它会导致事务环境中的一些不一致(即,我不确定如果失败的查询处于事务中间会发生什么)。

采用这种方式的示例配置是:

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的每个方法。使用闭合可以减轻那里的痛苦。

答案 3 :(得分:17)

您可以重置您的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();

答案 4 :(得分:17)

这就是我解决Doctrine " EntityManager已关闭的问题。" 问题。 基本上每次出现异常(即重复密钥)时,Doctrine都会关闭实体管理器。如果您仍想与数据库进行交互,则必须通过调用 JGrinon 所提及的resetManager()方法来重置实体管理器。

在我的应用程序中,我运行了多个RabbitMQ使用者,他们都在做同样的事情:检查数据库中是否存在实体,如果是,则返回它,如果不创建它然后返回它。 在检查该实体是否已经存在并创建它之间的几毫秒内,另一个消费者碰巧做了同样的事情并创建了丢失的实体,使另一个消费者出现了重复的密钥异常。

这导致了软件设计问题。基本上我想要做的是在一个事务中创建所有实体。这对大多数人来说可能是自然的,但在我的情况下肯定是概念错误的。考虑以下问题:我必须存储具有这些依赖关系的足球比赛实体。

  • 一组(即A组,B组......)
  • 一轮(即半决赛)
  • 场地(即比赛正在进行的体育场)
  • 匹配状态(即半场时间,全职)
  • 两队参加比赛
  • 比赛本身

现在,为什么场地创建应该与匹配在同一个交易中?可能是因为我刚刚收到一个新的地点而不在我的数据库中,所以我必须先创建它。但也可能是该场地可能会举办另一场比赛,因此另一位消费者可能会尝试同时创建它。所以我要做的是首先在单独的事务中创建所有依赖项,确保我在重复键异常中重置实体管理器。我要说匹配旁边的所有实体都可以定义为"共享"因为它们可能是其他消费者中其他交易的一部分。没有"共享的东西"在那里,匹配本身不可能同时由两个消费者创造。所以在上一次交易中,我希望看到比赛以及两支球队和比赛之间的关系。所有这些都导致了另一个问题。如果重置实体管理器,则在重置之前检索的所有对象都是全新的Doctrine。因此,Doctrine不会尝试在它们上运行 UPDATE ,而是 INSERT !因此,请确保在逻辑上正确的事务中创建所有依赖项,然后在将它们设置为目标实体之前从数据库中检索所有对象。请考虑以下代码作为示例:

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

我希望它有所帮助:)

答案 5 :(得分:4)

在控制器中。

异常关闭实体管理器。这会导致批量插入的麻烦。 要继续,需要重新定义它。

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

答案 6 :(得分:2)

我觉得这是值得的,因为在try / catch循环中捕获了我没有做任何事情的SQL错误(带有em->flush()),这在批处理导入命令中发生了。就我而言,这是因为我试图插入一条记录,该记录的非空属性保留为null。

通常,这将导致发生严重异常,并且命令或控制器停止运行,但是我只是记录此问题并继续进行。 SQL错误导致实体管理器关闭。

请检查您的dev.log文件中是否存在此类愚蠢的SQL错误,因为这可能是您的错。 :)

答案 7 :(得分:1)

尝试使用:

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

开始交易之前。

Connection::rollback方法上,它会检查nestTransactionsWithSavepoints属性。

答案 8 :(得分:0)

这是一个很老的问题,但我遇到了类似的问题。我正在做这样的事情:

// 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已关闭。

在我的情况下,解决方案只是明确了不同类型的实体并让$entityOne仍在EM下:

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

答案 9 :(得分:0)

这是你如何在 Symfony3 中重置 entityManager。如果它已经关闭,它应该重新打开 em:

在控制器中:

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

在服务中:

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

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

不要忘记在 service.yml 中注入“@doctrine”作为服务参数!

我想知道,如果不同的方法同时尝试同时访问同一个实体,是否会出现这个问题?

答案 10 :(得分:0)

相同的问题,可以通过简单的代码重构来解决。 当必填字段为null时,有时会出现问题,然后再尝试重构代码。更好的工作流程可以解决问题。

答案 11 :(得分:0)

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

这意味着代码中的某些错误导致:

Doctrine\ORM\ORMException: The EntityManager is closed.

所以检查日志是个好主意

答案 12 :(得分:0)

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

答案 13 :(得分:0)

我找到了有关此问题的有趣文章

    ArrayList<String> students = new ArrayList<String>(); 
    ArrayList<String> smartStudents = new ArrayList<String>(); 
    ArrayList<String> stupidStudents = new ArrayList<String>(); 
    ArrayList<String> oldStudents = new ArrayList<String>(); 
    ArrayList<String> youngStudents = new ArrayList<String>(); 


    //adding all the students to students list 
    Collections.addAll(students, "Ram", "Mohan", "Sohan", "Rabi", "Shabbir","Jack", "Johnson", "Peter", "Despina", "Me");
    //adding young students to youngStudents list 
    Collections.addAll(youngStudents, "Ram", "Mohan", "Sohan", "Rabi", "Shabbir");
    //adding smart students to oldStudents list 
    Collections.addAll(oldStudents, "Jack", "Johnson", "Peter", "Despina", "Me");
    //adding smart students to smartStudents list 
    Collections.addAll(smartStudents, "Sohan", "Rabi", "Peter", "Despina");
    //adding smart students to stupidStudents list 
    Collections.addAll(stupidStudents, "Ram", "Mohan", "Shabbir","Jack", "Johnson", "Me");

    Scanner input = new Scanner(System.in);
    String uInput = "";



    System.out.print("This is a students search engine, write 'young' for younger students and 'old' for older ones ");
    uInput = input.nextLine();

    if(uInput.equals("young")) {

        students.removeAll(oldStudents); 

    } else if (uInput.equals("old")) {

        students.removeAll(youngStudents);

    }


    System.out.print("now write 'Smart' for smarter students and 'Stupid' for less smart students ");
    uInput = input.nextLine();

  if(uInput.equals("smart")) {

        students.removeAll(stupidStudents); 

    } else if (uInput.equals("Stupid")) {

        students.removeAll(smartStudents);

    }


  System.out.println(students);

Doctrine 2 Exception EntityManager is closed

答案 14 :(得分:0)

Symfony v4.1.6

Doctrine v2.9.0

在存储库中插入重复项的过程

  1. 在您的存储库中访问注册表

//begin of repo

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

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

  1. 在发生异常的情况下,将风险代码包装到交易中,并让经理休息

//begin of repo

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

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

答案 15 :(得分:0)

我有这个问题。这就是我如何解决它。

尝试刷新或持续时,连接似乎已关闭。尝试重新打开它是一个糟糕的选择,因为会产生新的问题。我试着理解连接关闭的原因,发现我在持久化之前做了太多修改。

persist()之前解决了这个问题。

答案 16 :(得分:-1)

使用Symfony 5 / Doctrine 2时,我遇到了相同的错误。我的一个字段是使用MySQL保留字“ order”命名的,这导致DBALException。当您想使用保留字时,必须使用反引号将其名称转义。以注释形式:

$this->registerJs(
    "function setButtonValue(stringValue) { alert('Button clicked!'); });",
);
?>

答案 17 :(得分:-1)

The EntityManager is closed.

我遇到了同样的问题。原因是数据库表中缺少列 - 我只需要运行迁移。

答案 18 :(得分:-2)

我遇到了同样的问题。看了几个地方后我就是这样处理的。

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

希望这有助于某人!

答案 19 :(得分:-2)

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