Подтвердить что ты не робот

EntityManager закрыт

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

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

4b9b3361

Ответ 1

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

Ответ 2

Мое решение.

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

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

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

Ответ 3

Symfony 2.0:

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

Symfony 2.1 +:

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

Ответ 4

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

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

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

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

Теперь, почему создание места должно быть в той же транзакции, что и матч? Возможно, я только что получил новое место, которого нет в моей базе данных, поэтому мне нужно сначала его создать. Но может также случиться так, что в этом месте может быть проведен другой матч, так что другой потребитель, вероятно, попытается создать его в то же время. Поэтому мне нужно было сначала создать все зависимости в отдельных транзакциях, убедившись, что я сбрасываю диспетчер сущностей в исключении дублирующего ключа. Я бы сказал, что все сущности, находящиеся рядом с соответствием, могут быть определены как "общие", потому что они потенциально могут быть частью других транзакций других потребителей. Что-то, что там не "разделяется", - это само совпадение, которое вряд ли будет создано двумя потребителями одновременно. Так что в последней транзакции я ожидаю увидеть только матч и отношения между двумя командами и матчем.

Все это также привело к другой проблеме. Если вы сбрасываете Entity Manager, все объекты, которые вы извлекли до сброса, являются абсолютно новыми для 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();

Я надеюсь, что это помогает :)

Ответ 5

Вы можете 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();

Ответ 6

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

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++;
    }               
  }                      
}

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

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

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

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

Ответ 10

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

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

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

Ответ 11

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

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

Ответ 12

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

Ответ 14

Я столкнулся с той же проблемой при тестировании изменений в Symfony 4.3.2

Я понизил уровень журнала до INFO

И снова запустил тест

И залогинившийся показал это:

console.ERROR: Error thrown while running command "doctrine:schema:create". Message: "[Semantical Error] The annotation "@ORM\Id" in property App\Entity\Common::$id was never imported. Did you maybe forget to add a "use" statement for this annotation?" {"exception":"[object] (Doctrine\\Common\\Annotations\\AnnotationException(code: 0): [Semantical Error] The annotation \"@ORM\\Id\" in property App\\Entity\\Common::$id was never imported. Did you maybe forget to add a \"use\" statement for this annotation? at C:\\xampp\\htdocs\\dirty7s\\vendor\\doctrine\\annotations\\lib\\Doctrine\\Common\\Annotations\\AnnotationException.php:54)","command":"doctrine:schema:create","message":"[Semantical Error] The annotation \"@ORM\\Id\" in property App\\Entity\\Common::$id was never imported. Did you maybe forget to add a \"use\" statement for this annotation?"} []

Это означает, что какая-то ошибка в коде вызывает:

Doctrine\ORM\ORMException: The EntityManager is closed.

Так что это хорошая идея, чтобы проверить журнал

Ответ 15

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

Ответ 16

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

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

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