[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 연결 클래스를 만들고 Doctrine 클래스를 래핑하고 예외 처리를 제공하는 것입니다 (예 : EntityManager에 예외를 표시하기 전에 여러 번 재시도). 그것은 약간 해키하고 트랜잭션 환경에서 약간의 불일치가 발생할 수 있습니다 (즉, 실패한 쿼리가 트랜잭션 중간에 있으면 어떻게 될지 잘 모르겠습니다).
이러한 방식으로 사용할 수있는 구성의 예는 다음과 같습니다.
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 메서드를 재정의해야한다는 것입니다. 클로저를 사용하면 통증을 완화 할 수 있습니다.
답변
내 솔루션.
무엇이든 확인하기 전에 :
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();
답변
이것이 제가 “The EntityManager is closed”라는 교리를 해결 한 방법 입니다. 발행물. 기본적으로 예외 (예 : 중복 키)가 있거나 필수 열에 대한 데이터를 제공하지 않을 때마다 Doctrine은 엔티티 관리자를 닫습니다. 여전히 데이터베이스와 상호 작용하려면 JGrinon에서resetManager()
언급 한 메서드를 호출하여 Entity Manger를 재설정해야합니다 .
내 응용 프로그램에서 나는 모두 같은 일을하고있는 여러 RabbitMQ 소비자를 실행하고있었습니다. 엔티티가 데이터베이스에 있는지 확인하고 있다면 반환하고 생성하지 않으면 반환 한 다음 반환합니다. 해당 엔티티가 이미 존재하는지 확인하고 생성하는 사이 몇 밀리 초 동안 다른 소비자가 동일한 작업을 수행하고 누락 된 엔티티를 생성하여 다른 소비자에게 중복 키 예외 ( 경쟁 조건 )를 발생시킵니다.
이로 인해 소프트웨어 설계 문제가 발생했습니다. 기본적으로 내가하려는 것은 하나의 트랜잭션에서 모든 엔티티를 만드는 것입니다. 이것은 대부분의 경우 자연 스럽지만 제 경우에는 확실히 개념적으로 잘못되었습니다. 다음 문제를 고려하십시오. 이러한 종속성이있는 football Match 엔티티를 저장해야했습니다.
- 그룹 (예 : 그룹 A, 그룹 B …)
- 라운드 (예 : 준결승전 …)
- 장소 (예 : 경기가 열리는 경기장)
- 경기 상태 (예 : 전반전, 풀 타임)
- 경기를하는 두 팀
- 경기 자체
이제 장소 생성이 경기와 동일한 트랜잭션에 있어야하는 이유는 무엇입니까? 데이터베이스에없는 새 장소를 방금 받았을 수 있으므로 먼저 만들어야합니다. 그러나 해당 장소에서 다른 경기를 주최 할 수 있으므로 다른 소비자도 동시에 제작을 시도 할 수 있습니다. 그래서 내가해야 할 일은 중복 키 예외에서 엔티티 관리자를 재설정하고 있는지 확인하는 별도의 트랜잭션에서 먼저 모든 종속성을 만드는 것입니다. 일치 항목 옆에있는 모든 엔티티는 잠재적으로 다른 소비자의 다른 트랜잭션의 일부가 될 수 있기 때문에 “공유”로 정의 될 수 있습니다. “공유”되지 않는 것은 동시에 두 소비자가 만들지 않을 일치 자체가 있습니다.
이 모든 것이 또 다른 문제로 이어졌습니다. 엔티티 관리자를 재설정하면 재설정하기 전에 검색 한 모든 객체는 완전히 새로운 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();
나는 그것이 도움이되기를 바랍니다 🙂
답변
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();
답변
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();
}
}
답변
컨트롤러에서.
예외는 엔티티 관리자를 닫습니다. 이것은 대량 삽입에 문제를 일으 킵니다. 계속하려면 재정의해야합니다.
/**
* @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++;
}
}
}