EntityManager已关闭

问题描述 投票:67回答:15
[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();

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

symfony orm doctrine-orm entitymanager
15个回答
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的每个方法。使用闭合可以缓解那里的一些痛苦。


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

0
投票

Symphons int

学说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. 在发生异常时将风险代码包装到事务和休息管理器中



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


0
投票

我发现了一篇关于这个问题的有趣文章

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

Doctrine 2 Exception EntityManager is closed


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

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

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

希望这有助于某人!


53
投票

我的解决方案

在做任何事之前检查:

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

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


32
投票

Symfony 2.0:

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

Symfony 2.1+:

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

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

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

我希望它有帮助:)


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

1
投票

为了它的价值,我发现这个问题发生在批量导入命令中,因为try / catch循环捕获了一个我没有做任何事情的SQL错误(使用em->flush())。在我的情况下,这是因为我试图插入一个带有非可空属性的记录,保留为null。

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

检查你的dev.log文件是否有任何这样的愚蠢的SQL错误,因为它可能是你的错。 :)


0
投票

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

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

persist()早先解决了这个问题。


0
投票

尝试使用:

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

在开始交易之前。

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

© www.soinside.com 2019 - 2024. All rights reserved.