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

再接続する方法を考えている人はいますか?

4

21 に答える 21

80

私の解決策。

何かをする前にチェックしてください:

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

すべてのエンティティが保存されます。ただし、特定のクラスや場合によっては便利です。entitymanagerが挿入されたサービスがある場合でも、それは閉じられています。

于 2013-09-29T10:36:58.403 に答える
38

Symfony 2.0

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

Symfony 2.1+

$em = $this->getDoctrine()->resetManager();
于 2014-02-13T10:29:39.813 に答える
33

これが私が「EntityManagerが閉じられている」という教義を解決した方法です。問題。基本的に、例外がある(つまり、キーが重複している)たびに、または必須の列にデータを提供しないと、Doctrineはエンティティマネージャーを閉じます。それでもデータベースを操作したい場合は、 JGrinonresetManager()で説明されているように、メソッドを呼び出してエンティティマネージャーをリセットする必要があります。

私のアプリケーションでは、すべて同じことを行っている複数のRabbitMQコンシューマーを実行していました。エンティティがデータベースに存在するかどうかを確認し、存在する場合はそれを返し、そうでない場合は作成してから返します。そのエンティティがすでに存在するかどうかを確認してから作成するまでの数ミリ秒で、別のコンシューマーがたまたま同じことを行い、欠落しているエンティティを作成して、他のコンシューマーに重複キー例外(競合状態)が発生しました。

これにより、ソフトウェア設計の問題が発生しました。基本的に私がやろうとしていたのは、1つのトランザクションですべてのエンティティを作成することでした。これはほとんどの人にとって自然に感じるかもしれませんが、私の場合は間違いなく概念的に間違っていました。次の問題を考えてみましょう。これらの依存関係を持つサッカーの試合エンティティを保存する必要がありました。

  • グループ(例:グループA、グループB ...)
  • ラウンド(例:準決勝...)
  • 会場(つまり、試合が行われているスタジアム)
  • 試合状況(ハーフタイム、フルタイムなど)
  • 試合をしている2つのチーム
  • 試合自体

では、なぜ会場の作成は試合と同じトランザクションで行う必要があるのでしょうか。データベースにない新しい会場を受け取ったばかりなので、最初に作成する必要がある可能性があります。しかし、その会場が別の試合を主催する可能性もあるため、別の消費者も同時にそれを作成しようとする可能性があります。したがって、私がしなければならなかったのは、最初にすべての依存関係を別々のトランザクションで作成し、重複キーの例外でエンティティマネージャーをリセットしていることを確認することでした。一致の横にあるすべてのエンティティは、他のコンシューマーの他のトランザクションの一部である可能性があるため、「共有」として定義できます。そこに「共有」されていないものは、2人の消費者によって同時に作成される可能性が低い一致自体です。

これらすべてが別の問題にもつながりました。エンティティマネージャーをリセットすると、リセット前に取得したすべてのオブジェクトは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'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();

お役に立てば幸いです:)

于 2015-06-29T10:00:48.477 に答える
27

少なくともSymfony2.0とDoctrine2.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の各メソッドをオーバーライドする必要があることです。クロージャーを使用すると、そこでの痛みを和らげることができます。

于 2013-01-10T15:23:20.667 に答える
18

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();
于 2013-05-16T19:06:48.360 に答える
13

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();
    }
}
于 2019-06-17T12:36:05.043 に答える
6

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

于 2018-12-06T08:51:24.090 に答える
4

コントローラー内。

例外により、エンティティマネージャが閉じます。これは、バルク挿入の問題を引き起こします。続行するには、それを再定義する必要があります。

/** 
* @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++;
    }               
  }                      
}
于 2015-07-16T11:37:02.890 に答える
3

em->flush()この問題は、何もしなかったSQLエラー(with)をキャッチするtry / catchループが原因で、バッチインポートコマンドで発生していることがわかりました。私の場合は、null許容でないプロパティがnullのままになっているレコードを挿入しようとしたためです。

通常、これにより重大な例外が発生し、コマンドまたはコントローラーが停止しますが、代わりにこの問題をログに記録して続行していました。SQLエラーにより、エンティティマネージャが閉じていました。

dev.logあなたのせいである可能性があるので、このようなばかげたSQLエラーがないかファイルをチェックしてください。:)

于 2018-09-04T10:23:58.303 に答える
3

この問題に関する興味深い記事を見つけました

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

Doctrine2例外EntityManagerが閉じられました

于 2019-04-18T15:18:05.800 に答える
1

同じ問題が、単純なコードリファクタリングで解決されました。この問題は、必須フィールドがnullの場合に発生することがあります。これを行う前に、コードをリファクタリングしてみてください。より良いワークフローで問題を解決できます。

于 2020-10-16T06:51:21.890 に答える
1

そのプロパティのデフォルト値を設定せずに、null許容でない列を持つオブジェクトを永続化しようとしたときに同じ問題が発生しました。

/**
 * @ORM\Column(type="boolean")
 */
protected $isActive;

場合によっては、コントローラーでそのオブジェクトを準備するときに、次の方法でそのプロパティを明示的に設定しません。

$object->setIsActive(false);

したがって、最後に、データベースのその列のデフォルト値は0ですが、Doctrineはその列にNULLを保存しようとしました。

に変更した後:

/**
 * @ORM\Column(type="boolean")
 */
protected $isActive = false;

問題は消えました。

于 2021-09-29T22:03:19.183 に答える
1

Symfony 5 / Doctrine ORM 2.10では、このresetEntityManagerメソッドは使用できません。

正しい解決策として私が見つけたのはpersistflushメソッドをでラップすることでしたが、明示的なトランザクションtryを使用しました。

例外が発生した場合は、resetManagerメソッドを使用します。

次に例を示します。

try {
    $this->entityManager->beginTransaction();
    $this->entityManager->persist($entity);
    $this->entityManager->flush();
    $this->entityManager->commit();
} catch (Exception $e) {
    $this->entityManager->rollback();
    $this->managerRegistry->resetManager();
}

*managerRegistryDoctrine\Persistence\ManagerRegistry

出典:Doctrine'Slackの#ormチャネルでのこのメッセージ。

于 2021-12-08T05:15:56.037 に答える
0

この問題が発生しました。これは私がそれを修正した方法です。

フラッシュまたは永続化しようとしているときに、接続が閉じているようです。新しい問題が発生するため、再開しようとするのは悪い選択です。接続が閉じられた理由を理解しようとしましたが、永続化する前にあまりにも多くの変更を行っていたことがわかりました。

persist()は以前に問題を解決しました。

于 2015-02-13T12:58:17.830 に答える
0

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.

したがって、ログを確認することをお勧めします

于 2019-07-21T09:19:48.523 に答える
0

これは、Symfony3でentityManagerをリセットする方法です。閉じられている場合は、emを再度開く必要があります。

コントローラの場合:

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

サービス中:

  if (!$this->em->isOpen()) {
        $this->managerRegistry->resetManager('managername');
        $this->em = $this->managerRegistry->getManager('default');
    }

    $this->em->persist(...);

service.ymlにサービス引数として「@doctrine」を挿入することを忘れないでください!

異なるメソッドが同時に同じエンティティにアクセスしようとした場合にこの問題が発生するのではないかと思います。

于 2021-02-26T15:10:26.700 に答える
-1

これは本当に古い問題ですが、私も同様の問題を抱えていました。私はこのようなことをしていました:

// 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が閉じています。

私の場合の解決策は、異なるタイプのエンティティを明確にし、$entityOneEMの下に置いたままにすることでした。

$this->em->clear(SomeEntityClass::class);
于 2016-03-02T15:48:19.103 に答える
-1
The EntityManager is closed.

私も同じ問題を抱えていました。その理由は、データベースのテーブルに列がありませんでした-移行を実行する必要がありました。

于 2021-04-06T18:15:47.993 に答える
-2
// 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();
于 2014-09-08T22:00:29.690 に答える
-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();
}

これが誰かに役立つことを願っています!

于 2015-11-19T13:23:38.670 に答える
-3

Symfony 5 / Doctrine 2を使用しても同じエラーが発生しました。私のフィールドの1つに、MySQLの予約語「order」を使用して名前が付けられたため、DBALExceptionが発生しました。予約語を使用する場合は、バックティックを使用してその名前をエスケープする必要があります。注釈形式:

@ORM\Column(name="`order`", type="integer", nullable=false)
于 2020-02-10T15:42:39.143 に答える