[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 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的每个方法。使用闭合可以缓解那里的一些痛苦。
这是一个很老的问题,但我遇到了类似的问题。我做的是这样的:
// 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);
Symphons int
学说v2.9.0
进程在存储库中插入重复项
//begin of repo
/** @var RegistryInterface */
protected $registry;
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
parent::__construct($registry, YourEntity::class);
}
//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();
}
}
我发现了一篇关于这个问题的有趣文章
if (!$entityManager->isOpen()) {
$entityManager = $entityManager->create(
$entityManager->getConnection(), $entityManager->getConfiguration());
}
在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();
}
}
// 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();
我遇到了同样的问题。看了几个地方后我就是这样处理的。
//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();
}
希望这有助于某人!
我的解决方案
在做任何事之前检查:
if (!$this->entityManager->isOpen()) {
$this->entityManager = $this->entityManager->create(
$this->entityManager->getConnection(),
$this->entityManager->getConfiguration()
);
}
将保存所有实体。但它对特定类或某些情况很方便。如果你有一些注入了entitymanager的服务,它仍然会被关闭。
Symfony 2.0:
$em = $this->getDoctrine()->resetEntityManager();
Symfony 2.1+:
$em = $this->getDoctrine()->resetManager();
您可以重置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();
这就是我解释Doctrine“EntityManager关闭”的原因。问题。基本上每次出现异常(即重复密钥)时,Doctrine都会关闭实体管理器。如果您仍想与数据库进行交互,则必须通过调用JGrinon提到的resetManager()
方法来重置实体管理器。
在我的应用程序中,我运行了多个RabbitMQ使用者,他们都在做同样的事情:检查数据库中是否存在实体,如果是,则返回它,如果不创建它然后返回它。在检查该实体是否已经存在并创建它之间的几毫秒内,另一个消费者碰巧做了同样的事情,并创建了丢失的实体,使另一个消费者招致了一个重复的密钥异常。
这导致了软件设计问题。基本上我想要做的是在一个事务中创建所有实体。这对大多数人来说可能是自然的,但在我的情况下肯定是概念错误的。考虑以下问题:我必须存储具有这些依赖关系的足球比赛实体。
现在,为什么场地创建应该与匹配在同一个交易中?可能是因为我刚刚收到一个新的场所,它不在我的数据库中所以我必须先创建它。但也可能是该场地可能会举办另一场比赛,因此另一位消费者可能会尝试同时创建它。所以我要做的是首先在单独的事务中创建所有依赖项,确保我在重复键异常中重置实体管理器。我会说匹配旁边的所有实体都可以定义为“共享”,因为它们可能是其他消费者中其他交易的一部分。在那里没有“共享”的东西是匹配本身,不可能同时由两个消费者创建。所以在上一次交易中,我希望看到比赛以及两支球队和比赛之间的关系。所有这些都导致了另一个问题。如果重置实体管理器,则在重置之前检索到的所有对象都是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();
我希望它有帮助:)
在控制器中。
例外关闭了实体管理器。这会导致批量插入的麻烦。要继续,需要重新定义它。
/**
* @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++;
}
}
}
为了它的价值,我发现这个问题发生在批量导入命令中,因为try / catch循环捕获了一个我没有做任何事情的SQL错误(使用em->flush()
)。在我的情况下,这是因为我试图插入一个带有非可空属性的记录,保留为null。
通常这会导致发生严重异常并且命令或控制器停止,但我只是记录此问题并继续进行。 SQL错误导致实体管理器关闭。
检查你的dev.log
文件是否有任何这样的愚蠢的SQL错误,因为它可能是你的错。 :)
我有这个问题。这是我如何解决它。
尝试刷新或持续时,连接似乎已关闭。尝试重新打开它是一个糟糕的选择,因为会产生新的问题。我试着理解连接关闭的原因,发现我在持久化之前做了太多修改。
persist()早先解决了这个问题。
尝试使用:
$em->getConnection()->[setNestTransactionsWithSavepoints][1](true);
在开始交易之前。
在Connection::rollback
方法上,它检查nestTransactionsWithSavepoints
属性。