这就是我解决原则“ EntityManager已关闭”的方式。问题。基本上,每次出现异常(即重复键)或不为强制性列提供数据时,都会导致Doctrine关闭实体管理器。如果仍要与数据库进行交互,则必须通过调用JGrinonresetManager()
提到的方法来重置Entity Manger 。
在我的应用程序中,我正在运行多个RabbitMQ使用者,它们都在做相同的事情:检查数据库中是否存在实体,如果是,则返回它,如果不创建,则返回它。在检查该实体是否存在与创建该实体之间的几毫秒内,另一个使用方碰巧执行了同样的操作,并创建了丢失的实体,从而使另一个使用方发生重复的关键异常(竞争条件)。
这导致了软件设计问题。基本上我想做的是在一个事务中创建所有实体。对于大多数人来说,这似乎很自然,但是在我看来,这绝对是错误的。考虑以下问题:我必须存储一个具有这些依赖性的足球比赛实体。
- 一个组(例如,A组,B组...)
- 一回合(例如半决赛...)
- 场地(即比赛进行的体育场)
- 比赛状态(例如半场,全场)
- 两队比赛
- 比赛本身
现在,为什么场地创建应该与比赛在同一笔交易中?可能是因为我刚刚收到一个不在数据库中的新地点,所以我必须先创建它。但也可能是该地点可能举办另一场比赛,因此另一位消费者也可能会尝试同时创作。因此,我要做的是首先在单独的事务中创建所有依赖项,以确保我在重复的键异常中重置实体管理器。我要说的是,匹配项旁边的所有实体都可以定义为“共享”,因为它们可能是其他使用者中其他交易的一部分。匹配项本身不是“共享”的,它不可能由两个使用者同时创建。
所有这些还导致了另一个问题。如果重置实体管理器,则重置之前检索的所有对象对于Doctrine来说都是全新的。因此,Doctrine不会尝试对它们运行UPDATE,而是尝试执行INSERT!因此,请确保您在逻辑上正确的事务中创建所有依赖项,然后在将它们设置为目标实体之前从数据库中检索所有对象。考虑以下代码作为示例:
$group = $this->createGroupIfDoesNotExist($groupData);
$match->setGroup($group);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
所以这就是我认为应该做的。
$group = $this->createGroupIfDoesNotExist($groupData);
$venue = $this->createVenueIfDoesNotExist($venueData);
$round = $this->createRoundIfDoesNotExist($roundData);
$group = $this->getGroup($groupData);
$venue = $this->getVenue($venueData);
$round = $this->getGroup($roundData);
$match->setGroup($group);
$match->setVenue($venue);
$match->setRound($round);
$matchTeamHome = new MatchTeam();
$matchTeamHome->setMatch($match);
$matchTeamHome->setTeam($teamHome);
$matchTeamAway = new MatchTeam();
$matchTeamAway->setMatch($match);
$matchTeamAway->setTeam($teamAway);
$match->addMatchTeam($matchTeamHome);
$match->addMatchTeam($matchTeamAway);
$em->persist($match);
$em->persist($matchTeamHome);
$em->persist($matchTeamAway);
$em->flush();
希望对您有所帮助:)