这就是我解决 Doctrine "The EntityManager is closed." 问题的方法。
基本上每次出现异常(即重复键)或不为必填列提供数据时,都会导致 Doctrine 关闭实体管理器。如果您仍想与数据库交互,您必须通过调用 JGrinon 提到的 resetManager() 方法来重置实体管理器。
在我的应用程序中,我正在运行多个 RabbitMQ 消费者,它们都在做同样的事情:检查数据库中是否存在实体,如果是则返回它,如果没有则创建它然后返回它。
在检查该实体是否已经存在和创建它之间的几毫秒内,另一个消费者碰巧做了同样的事情并创建了缺失的实体,从而使另一个消费者产生重复键异常(竞争条件)。 p>
这导致了软件设计问题。基本上我想做的是在一个事务中创建所有实体。这对大多数人来说可能感觉很自然,但在我的情况下,这在概念上肯定是错误的。考虑以下问题:我必须存储一个具有这些依赖关系的足球比赛实体。
- 一个组(例如 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();
希望对你有帮助:)