これが私が「EntityManagerが閉じられている」という教義を解決した方法です。問題。基本的に、例外(つまり、重複キー)があるか、必須列にデータを提供しないたびに、Doctrineはエンティティマネージャーを閉じます。それでもデータベースを操作したい場合はresetManager()
、JGrinonが説明したメソッドを呼び出して、エンティティマネージャーをリセットする必要があります。。
私のアプリケーションでは、すべて同じことをしている複数のRabbitMQコンシューマーを実行していました。エンティティがデータベースに存在するかどうかを確認し、存在する場合はそれを返し、そうでない場合は作成してから返します。そのエンティティがすでに存在するかどうかを確認してから作成するまでの数ミリ秒で、別のコンシューマーがたまたま同じことを行い、欠落しているエンティティを作成して、他のコンシューマーに重複キー例外(競合状態)が発生しました。)が。
これは、ソフトウェア設計の問題につながりました。基本的に私がやろうとしていたのは、1つのトランザクションですべてのエンティティを作成することでした。これはほとんどの人にとって自然に感じるかもしれませんが、私の場合は間違いなく概念的に間違っていました。次の問題を考えてみましょう。これらの依存関係を持つサッカーの試合エンティティを保存する必要がありました。
- グループ(例:グループA、グループB ...)
- ラウンド(例:準決勝...)
- 会場(つまり、試合が行われているスタジアム)
- 試合状況(ハーフタイム、フルタイムなど)
- 試合をしている2つのチーム
- 試合自体
では、なぜ会場の作成は試合と同じトランザクションで行う必要があるのでしょうか。データベースにない新しい会場を受け取ったばかりなので、最初に作成する必要がある可能性があります。しかし、その会場が別の試合を主催する可能性もあるため、別の消費者も同時にそれを作成しようとする可能性があります。したがって、私がしなければならなかったのは、最初にすべての依存関係を別々のトランザクションで作成し、重複キーの例外でエンティティマネージャーをリセットしていることを確認することでした。一致の横にあるすべてのエンティティは、他のコンシューマーの他のトランザクションの一部である可能性があるため、「共有」として定義できます。そこに「共有」されていないものは、2人の消費者によって同時に作成される可能性が低い一致自体です。
これらすべてが別の問題にもつながりました。エンティティマネージャーをリセットすると、リセットする前に取得したすべてのオブジェクトは、まったく新しい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();
私はそれが役立つことを願っています:)