EntityManager kapatıldı


87
[Doctrine\ORM\ORMException]   
The EntityManager is closed.  

Veri eklerken bir DBAL istisnası aldıktan sonra EntityManager kapanıyor ve onu yeniden bağlayamıyorum.

Böyle denedim ama bağlantı kurulamadı.

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

Nasıl yeniden bağlanılacağını bilen var mı?


Varlık yöneticisi neden kapanıyor?
Jay Sheth

2
@JaySheth Varlık yöneticisi, bir DBAL istisnasından sonra veya bir yıkamadan önce bir EntityManager-> clear () yapıyorsanız kapanabilir. Yürütme akışını dallara ayırmak için DBAL istisnalarını kullanan ve ardından EntityManager kapalı hatasıyla sonuçlanan bazı kişiler gördüm. Bu hatayı alıyorsanız, programınızdaki yürütme akışında bir sorun var demektir.
ILikeTacos

5
@AlanChavez - Bu hatayı alıyorum çünkü Doctrine'i aynı anda birden fazla iş parçacığı tarafından erişilen bir tabloya semafor bayrağı yazmak için kullanıyorum. MySQL semaforu yaratmaya çalışan iki rakip iş parçacığından birinde hata verecektir, çünkü anahtar kısıtlaması yalnızca birinin başarılı olabileceği anlamına gelir. IMO, Doctrine'de beklenen MySQL hatalarını güvenli bir şekilde işlemenize izin vermeyen bir kusur vardır . Neden bir INSERT ifadesinde çakışma olduğu için MySQL bağlantısının tamamı kesilsin?
StampyCode

2
Bu hatayı, istisnaları bir veritabanına kaydetmeye çalışıyorsanız da görürsünüz, app.exception_listenerancak istisna (kısıtlama ihlali gibi) bağlantıyı kapatmıştır.
Lg102

Yanıtlar:


25

En azından Symfony 2.0 ve Doctrine 2.1 için EntityManager'ı kapandıktan sonra yeniden açmak hiçbir şekilde mümkün olmadığından, bu çok zor bir sorundur.

Bu sorunun üstesinden gelmenin tek yolu, kendi DBAL Bağlantı sınıfınızı yaratmak, Doktrini bir sınıf haline getirmek ve istisna işleme sağlamaktır (örneğin, istisnayı EntityManager'a göndermeden önce birkaç kez yeniden denemek). Biraz karmaşık ve korkarım işlem ortamlarında bazı tutarsızlıklara neden olabilir (yani, başarısız sorgu bir işlemin ortasındaysa ne olacağı konusunda gerçekten emin değilim).

Bu yol için örnek bir konfigürasyon şudur:

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

Ders aşağı yukarı şu şekilde başlamalıdır:

namespace Your\DBAL;

class ReopeningConnectionWrapper extends Doctrine\DBAL\Connection {
  // ...
}

Çok can sıkıcı bir şey, istisna işleme sarmalayıcınızı sağlayan her Bağlantı yöntemini geçersiz kılmanız gerektiğidir. Kapakları kullanmak orada biraz ağrıyı hafifletebilir.


74

Çözümüm.

Herhangi bir şeyi kontrol etmeden önce:

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

Tüm varlıklar kaydedilecek. Ancak belirli sınıflar veya bazı durumlar için kullanışlıdır. Entitymanager enjekte edilmiş bazı hizmetleriniz varsa, yine de kapalı olacaktır.


bu, di konteynerin kendisi mevcut olmadığında çok daha iyidir. Teşekkür ederim.
Hari KT

1
3. parametrede $ this-> entityManager-> getEventManager () 'ı da geçirmek isteyebilirsiniz.
Medhat Gayed

34

Symfony 2.0 :

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

Symfony 2.1+ :

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

6
UYARI: resetEntityManager Symfony 2.1'den beri kullanımdan kaldırılmıştır. resetManagerBunun yerine kullanın
Francesco Casula

Bu, İş Birimi'ni de sıfırlar mı?
grip

@flu EntityManager sınıfının UnitOfWork sınıfını yönettiğini düşünürsek, bunun olacağından şüpheleniyorum. Ancak bunu test etmedim, bu yüzden emin olamıyorum.
Ryall

28

"EntityManager kapalı" Doktrini bu şekilde çözüldü . konu. Temel olarak, her seferinde bir istisna olduğunda (yani anahtarın kopyası) veya zorunlu bir sütun için veri sağlanmaması, Doktrinin Varlık Yöneticisini kapatmasına neden olacaktır. Hala veritabanı ile etkileşim kurmak istiyorsanız, JGrinonresetManager() tarafından belirtildiği gibi yöntemi çağırarak Varlık Yöneticisini sıfırlamanız gerekir .

Uygulamamda, hepsi aynı şeyi yapan birden fazla RabbitMQ tüketicisi çalıştırıyordum: veritabanında bir varlığın olup olmadığını kontrol ediyor, eğer evet ise, yaratmamışsa geri ver ve sonra iade et. O varlığın zaten var olup olmadığını kontrol etmekle onu oluşturmak arasındaki birkaç milisaniye içinde başka bir tüketici de aynısını yaptı ve eksik varlığı yaratarak diğer tüketiciyi yinelenen bir anahtar istisnasına ( yarış durumu ) maruz bıraktı .

Bu, bir yazılım tasarım sorununa yol açtı. Temelde yapmaya çalıştığım şey tüm varlıkları tek bir işlemde yaratmaktı. Bu çoğu kişiye doğal gelebilir ama benim durumumda kesinlikle kavramsal olarak yanlıştı. Şu sorunu düşünün: Bu bağımlılıklara sahip bir futbol Maçı varlığını depolamam gerekiyordu.

  • bir grup (örn. Grup A, Grup B ...)
  • bir tur (örn. Yarı finaller ...)
  • bir mekan (yani maçın yapıldığı stadyum)
  • bir maç durumu (örneğin ilk yarı, maç sonu)
  • maç oynayan iki takım
  • maçın kendisi

Şimdi, neden mekan yaratımı maçla aynı işlemde olmalı? Veritabanımda olmayan yeni bir mekan almış olabilirim, bu yüzden önce onu oluşturmalıyım. Ama aynı zamanda o yer başka bir maça ev sahipliği yapabilir, böylece başka bir tüketici muhtemelen aynı anda onu yaratmaya çalışacaktır. Bu yüzden yapmam gereken, tüm bağımlılıkları önce ayrı işlemlerde oluşturmak ve varlık yöneticisini yinelenen bir anahtar istisnasında sıfırladığımdan emin olmaktı. Eşleşmenin yanındaki tüm varlıkların "paylaşılan" olarak tanımlanabileceğini söyleyebilirim çünkü bunlar potansiyel olarak diğer tüketicilerdeki diğer işlemlerin parçası olabilirler. Orada "paylaşılmayan" bir şey, muhtemelen aynı anda iki tüketici tarafından yaratılmayacak olan maçın kendisidir.

Bütün bunlar başka bir soruna da yol açtı. Varlık Yöneticisini sıfırlarsanız, sıfırlamadan önce aldığınız tüm nesneler tamamen yeni Doctrine içindir. Yani Doctrine, bir GÜNCELLEME çalıştırmayı denemeyecek, ancak INSERT ! Bu nedenle, tüm bağımlılıklarınızı mantıksal olarak doğru işlemlerde oluşturduğunuzdan ve ardından tüm nesnelerinizi hedef varlığa ayarlamadan önce veritabanından geri aldığınızdan emin olun. Aşağıdaki kodu bir örnek olarak düşünün:

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

İşte bu şekilde yapılması gerektiğini düşünüyorum.

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

Umut ediyorum bu yardım eder :)


Harika bir açıklama. Benzer bir şey buldum ve cevabınıza katkıda bulunmanın güzel olacağını düşündüm. Çok teşekkür ederim.
Anjana Silva

17

EM'nizi sıfırlayabilirsiniz, böylece

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

10

In Symfony 4.2+ paketi kullanmak zorunda:

composer require symfony/proxy-manager-bridge

Aksi takdirde istisnayı alırsınız:

Resetting a non-lazy manager service is not supported. Declare the "doctrine.orm.default_entity_manager" service as lazy.  

Daha sonra, entityManager'ı şu şekilde sıfırlayabilirsiniz:

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

4

Denetleyicide.

İstisna, Varlık Yöneticisini kapatır. Bu, toplu ekleme için sorun yaratır. Devam etmek için yeniden tanımlamanız gerekiyor.

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

2

Ne em->flush()olursa olsun , bu sorunun, hakkında hiçbir şey yapmadığım bir SQL hatasını (ile ) yakalayan bir dene / yakala döngüsü nedeniyle bir toplu içe aktarma komutunda meydana geldiğini buldum. Benim durumumda bunun nedeni, null olarak bırakılamayan bir özelliği olan bir kayıt eklemeye çalıştığım içindir.

Tipik olarak bu kritik bir istisnanın olmasına ve komutun veya denetleyicinin durmasına neden olur, ancak bunun yerine bu sorunu günlüğe kaydediyor ve devam ediyordum. SQL hatası varlık yöneticisinin kapanmasına neden olmuştu.

dev.logDosyanızda bunun gibi aptalca SQL hataları olup olmadığını kontrol edin, çünkü bu sizin hatanız olabilir. :)



1

Symfony 4.3.2'deki değişiklikleri test ederken aynı problemle karşılaştım.

Günlük düzeyini INFO'ya düşürdüm

Ve testi tekrar yaptım

Ve günlüğe kaydedilenler şunu gösterdi:

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?"} []

Bu, koddaki bazı hataların şunlara neden olduğu anlamına gelir:

Doctrine\ORM\ORMException: The EntityManager is closed.

Bu nedenle günlüğü kontrol etmek iyi bir fikirdir.


İlki ikinciyle nasıl ilişkili olduğu hakkında ek bilgi verebilir misiniz?
George Novik

1

Symfony v4.1.6

Doktrin v2.9.0

Bir arşive kopyaları ekleme işlemi

  1. Deponuzdaki bir kayıt defterine erişin


    //begin of repo
    
    /** @var RegistryInterface */
    protected $registry;
    
    public function __construct(RegistryInterface $registry)
    {
        $this->registry = $registry;
        parent::__construct($registry, YourEntity::class);
    }

  1. Riskli kodu işlemin içine sarın ve istisna durumunda yöneticiyi sıfırlayın


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

Bu sorunu yaşadım. Ben böyle düzelttim.

Sifonu çekmeye veya ısrar etmeye çalışırken bağlantı kapanıyor gibi görünüyor. Yeniden açmaya çalışmak kötü bir seçimdir çünkü yeni sorunlar yaratır. Bağlantının neden kapatıldığını anlamaya çalıştım ve ısrar etmeden önce çok fazla değişiklik yaptığımı fark ettim.

persistan () sorunu daha önce çözdü.


0

Bu gerçekten eski bir mesele, ama ben de benzer bir sorun yaşadım. Bunun gibi bir şey yapıyordum:

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

Sorun, birincisi de dahil olmak üzere tüm varlıkları ayırıp hata atmasıydı EntityManager kapatıldı.

Benim durumumda çözüm , farklı Varlık türlerini netleştirmek ve $entityOnehala EM altında bırakmaktı :

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

Doctrine \ ORM \ EntityManager :: clear () 'i belirli varlıkları temizlemek için herhangi bir argümanla çağırmak artık kullanılmıyor ve Doctrine ORM 3.0
Foued MOUSSI

0

Aynı sorun, basit bir kod yeniden düzenleme ile çözüldü. Sorun bazen gerekli bir alan boş olduğunda, anithing yapmadan önce kodunuzu yeniden düzenlemeyi deneyin. Daha iyi bir iş akışı sorunu çözebilir.


-1

Symfony 5 / Doctrine 2'yi kullanırken de aynı hatayı yaşadım. Alanlarımdan biri, bir MySQL ayrılmış kelime "order" kullanılarak adlandırıldı ve DBALException'a neden oldu. Ayrılmış bir kelime kullanmak istediğinizde, geri tiklerini kullanarak isminden kaçmanız gerekir. Ek açıklama formunda:

@ORM\Column(name="`order`", type="integer", nullable=false)

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

-2

Ben de aynı problemle karşılaştım. Burada birkaç yere baktıktan sonra onunla nasıl başa çıktığımı görüyorum.

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

Umarım bu birine yardımcı olur!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.