EntityManager закрыт

57
[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();

Кто-нибудь знает, как подключиться?

  • 0
    Почему менеджер предприятия закрывается?
  • 2
    @JaySheth Менеджер сущностей может закрыться после исключения DBAL или если вы выполняете EntityManager-> clear () до сброса. Я видел, как некоторые люди использовали исключения DBAL для ветвления потока выполнения, а затем заканчивали закрытой ошибкой EntityManager. Если вы получаете эту ошибку, значит что-то не так в потоке выполнения вашей программы.
Показать ещё 2 комментария
Теги:
orm
entitymanager
doctrine2

13 ответов

22
Лучший ответ

Это очень сложная проблема, поскольку, по крайней мере, для 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, обеспечивающий вашу оболочку обработки исключений. Использование закрытий может облегчить боль там.

  • 1
    Большое спасибо!
47

Мое решение.

Прежде чем делать что-либо, проверьте:

if (!$this->entityManager->isOpen()) {
    $this->entityManager = $this->entityManager->create(
        $this->entityManager->getConnection(),
        $this->entityManager->getConfiguration()
    );
}

Все объекты будут сохранены. Но это удобно для определенного класса или некоторых случаев. Если у вас есть некоторые услуги с инъецированным сущ., Он все равно будет закрыт.

  • 0
    это намного лучше, когда сам контейнер di недоступен. Спасибо.
  • 0
    Вы также можете передать $ this-> entityManager-> getEventManager () в 3-й параметр.
28

Symfony 2.0:

$em = $this->getDoctrine()->resetEntityManager();

Symfony 2.1 +:

$em = $this->getDoctrine()->resetManager();
  • 5
    ВНИМАНИЕ: resetEntityManager устарел с Symfony 2.1. resetManager этого используйте resetManager
  • 0
    Это также сбрасывает единицу работы?
Показать ещё 1 комментарий
16

Вы можете reset ваш 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();
13

Вот как я решил Doctrine "EntityManager закрыт".. В основном каждый раз, когда исключение (т.е. Дубликат ключа), Doctrine закрывает Entity Manager. Если вы все еще хотите взаимодействовать с базой данных, вы должны reset Entity Manger, вызвав метод resetManager(), как указано JGrinon.

В моем приложении у меня было несколько пользователей RabbitMQ, которые все делали одно и то же: проверяя, существовал ли объект в базе данных, если да, верните его, если не создать его, а затем вернуть. В течение нескольких миллисекунд между проверкой того, существовал ли этот объект и его создание, другой потребитель сделал то же самое и создал отсутствующий объект, делающий другого потребителя в двойном ключевом исключении.

Это привело к проблеме разработки программного обеспечения. В основном то, что я пытался сделать, это создание всех объектов в одной транзакции. Это может показаться естественным для большинства, но в моем случае было концептуально неправильным. Рассмотрим следующую проблему: мне пришлось хранить футбольную фигуру Match, которая имела эти зависимости.

  • группа (т.е. группа A, группа B...)
  • раунд (т.е. полуфинал)
  • место (например, стадион, на котором проводится матч).
  • статус соответствия (т.е. половина времени, полный рабочий день)
  • две команды, играющие в матч
  • сам матч

Теперь, почему создание места должно быть в той же транзакции, что и матч? Возможно, я только что получил новое место, которое не было в моей базе данных, поэтому я должен создать его в первую очередь. Но также может случиться так, что в этом месте может быть другой матч, поэтому другой потребитель, вероятно, попытается создать его одновременно. Поэтому мне нужно было сначала создать все зависимости в отдельных транзакциях, чтобы я сбросил диспетчер объектов в дубликат ключевого исключения. Я бы сказал, что все сущности, находящиеся рядом с матчем, могут быть определены как "общие", поскольку они потенциально могут быть частью других транзакций у других потребителей. Что-то, что не "разделено", есть сам матч, который вряд ли будет создан двумя пользователями одновременно. Поэтому в последней транзакции я ожидаю увидеть только матч и связь между двумя командами и результатом. Все это также привело к еще одному вопросу. Если вы reset Менеджер сущностей, все объекты, которые вы извлекли до сброса, для 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 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();

Надеюсь, это поможет:)

4

В контроллере.

Exception закрывает диспетчер сущностей. Это создает проблемы для объемной вставки. Чтобы продолжить, нужно переопределить его.

/** 
* @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++;
    }               
  }                      
}
1

Насколько мне известно, эта проблема произошла в команде пакетного импорта из-за того, что цикл try/catch обнаруживает ошибку SQL (с помощью em->flush()), о которой я ничего не делал. В моем случае это было потому, что я пытался вставить запись с не-nullable свойством, оставленным как null.

Обычно это может привести к критическому исключению, и команда или контроллер должны остановиться, но я просто зарегистрировал эту проблему и продолжаю. Ошибка SQL заставила менеджера объектов закрыть.

Проверьте файл dev.log на любые глупые ошибки SQL, подобные этому, поскольку это может быть вашей ошибкой. :)

1

Попробуйте использовать:

$em->getConnection()->[setNestTransactionsWithSavepoints][1](true);

перед началом транзакции.

В режиме Connection::rollback он проверяет свойство nestTransactionsWithSavepoints.

  • 3
    Можете ли вы расширить это?
0

Symfony v4.1.6

Doctrine v2.9.0

Процесс вставки дубликатов в репозиторий

  1. Получите доступ к реестру в своем репо



//begin of repo

/** @var RegistryInterface */
protected $registry;

public function __construct(RegistryInterface $registry)
{
    $this->registry = $registry;
    parent::__construct($registry, YourEntity::class);
}

  1. Оберните рискованный код в транзакцию и отдохнувший менеджер в случае исключения



//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();
    }
}

0

Это действительно старая проблема, но у меня была аналогичная проблема. Я делал что-то вроде этого:

// 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.

В моем случае решение должно было просто прояснить отдельный тип Entity и оставить $entityOne еще в EM:

$this->em->clear(SomeEntityClass::class);
0

У меня была эта проблема. Это как я его исправил.

Соединение, похоже, закрывается при попытке сброса или сохранения. Попытка возобновить его - это плохой выбор, потому что создает новые проблемы. Я попытался понять, почему соединение было закрыто, и обнаружил, что я делаю слишком много изменений до того, как это будет продолжаться.

persist() ранее решила проблему.

-2

Я столкнулся с той же проблемой. Посмотрев на несколько мест, вот как я с этим справился.

//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();
}

Надеюсь, это поможет кому-то!

-3
// 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();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню