IdentityMap #7196

Open
opened 2026-01-22 15:46:38 +01:00 by admin · 14 comments
Owner

Originally created by @tasselchof on GitHub (Aug 2, 2023).

Bug Report

Q A
Version 2.16.0

Summary

I am getting an exception about entity in identity map

Current behavior

While adding an entity of class Proxy was already present for the same ID. This exception\nis a safeguard against an internal inconsistency - IDs should uniquely map to\nentity object instances. This problem may occur if:\n\n- you use application-provided IDs and reuse ID values;\n- database-provided IDs are reassigned after truncating the database without \n clearing the EntityManager;\n- you might have been using EntityManager#getReference() to create a reference \n for a nonexistent ID that was subsequently (by the RDBMS) assigned to another \n entity. \n\nOtherwise, it might be an ORM-internal inconsistency, please report it.

How to reproduce

I am not sure how to reproduce it, but I am getting this in that case:

I am getting some values from database with native query (I am using composite keys) and generating reference from it (I can fetch from database this value - no matter, everything is fine):

$productOffer = $this->getObjectManager()->getReference(
    Offer::class,
    [
        'shop' => $locationLog['sclr_0'],
        'id'   => $locationLog['sclr_1'],
    ]
);

I am using this reference in further request and if I generate spl_object_id it's always same object. But later I am fetching one another entity from database:

$acceptanceItem = $this->getObjectManager()->getRepository(
    Acceptance\Item::class
)->find($acceptanceItem);

This entity contains property productOffer with is linked with the same ids (so technically it contains same object).

And if I am checking spl_object_id($acceptanceItem->getProductOffer()) it's different object.

I am using the value of this object in preFlush to populate search index:

public function preFlush(PreFlushEventArgs $args)
    {
        $extendedValues = [];
        if (! empty($this->getProductOffer())) {
            $productOffer = $this->getProductOffer();

            $extendedValues[] = $productOffer->getName();
            $extendedValues[] = $productOffer->getSku();
            $extendedValues[] = $productOffer->getArticle();

And exactly here I am getting this exception:

#0  Doctrine\ORM\UnitOfWork->addToIdentityMap() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:3399]
#1  Doctrine\ORM\UnitOfWork->registerManaged() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php:692]
#2  Doctrine\ORM\Internal\Hydration\AbstractHydrator->registerManaged() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php:169]
#3  Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator->hydrateRowData() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php:65]
#4  Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator->hydrateAllData() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php:270]
#5  Doctrine\ORM\Internal\Hydration\AbstractHydrator->hydrateAll() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:779]
#6  Doctrine\ORM\Persisters\Entity\BasicEntityPersister->load() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:789]
#7  Doctrine\ORM\Persisters\Entity\BasicEntityPersister->loadById() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php:215]
#8  Doctrine\ORM\Proxy\ProxyFactory->Doctrine\ORM\Proxy\{closure}()
#9  Closure->__invoke() called at [/home/dev/domains/alpha.eu/data/DoctrineORMModule/Proxy/__CG__OrderadminProductsEntityProductOffer.php:318]

Expected behavior

I suppose should be the same object as it is already loaded before flush.

Originally created by @tasselchof on GitHub (Aug 2, 2023). ### Bug Report | Q | A |------------ | ------ | Version | 2.16.0 #### Summary I am getting an exception about entity in identity map #### Current behavior While adding an entity of class Proxy was already present for the same ID. This exception\nis a safeguard against an internal inconsistency - IDs should uniquely map to\nentity object instances. This problem may occur if:\n\n- you use application-provided IDs and reuse ID values;\n- database-provided IDs are reassigned after truncating the database without \n clearing the EntityManager;\n- you might have been using EntityManager#getReference() to create a reference \n for a nonexistent ID that was subsequently (by the RDBMS) assigned to another \n entity. \n\nOtherwise, it might be an ORM-internal inconsistency, please report it. #### How to reproduce I am not sure how to reproduce it, but I am getting this in that case: I am getting some values from database with native query (I am using composite keys) and generating reference from it (I can fetch from database this value - no matter, everything is fine): ```php $productOffer = $this->getObjectManager()->getReference( Offer::class, [ 'shop' => $locationLog['sclr_0'], 'id' => $locationLog['sclr_1'], ] ); ``` I am using this reference in further request and if I generate spl_object_id it's always same object. But later I am fetching one another entity from database: ```php $acceptanceItem = $this->getObjectManager()->getRepository( Acceptance\Item::class )->find($acceptanceItem); ``` This entity contains property productOffer with is linked with the same ids (so technically it contains same object). And if I am checking `spl_object_id($acceptanceItem->getProductOffer())` it's different object. I am using the value of this object in preFlush to populate search index: ```php public function preFlush(PreFlushEventArgs $args) { $extendedValues = []; if (! empty($this->getProductOffer())) { $productOffer = $this->getProductOffer(); $extendedValues[] = $productOffer->getName(); $extendedValues[] = $productOffer->getSku(); $extendedValues[] = $productOffer->getArticle(); ``` And exactly here I am getting this exception: ``` #0 Doctrine\ORM\UnitOfWork->addToIdentityMap() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:3399] #1 Doctrine\ORM\UnitOfWork->registerManaged() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php:692] #2 Doctrine\ORM\Internal\Hydration\AbstractHydrator->registerManaged() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php:169] #3 Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator->hydrateRowData() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php:65] #4 Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator->hydrateAllData() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php:270] #5 Doctrine\ORM\Internal\Hydration\AbstractHydrator->hydrateAll() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:779] #6 Doctrine\ORM\Persisters\Entity\BasicEntityPersister->load() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:789] #7 Doctrine\ORM\Persisters\Entity\BasicEntityPersister->loadById() called at [/home/dev/domains/alpha.eu/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php:215] #8 Doctrine\ORM\Proxy\ProxyFactory->Doctrine\ORM\Proxy\{closure}() #9 Closure->__invoke() called at [/home/dev/domains/alpha.eu/data/DoctrineORMModule/Proxy/__CG__OrderadminProductsEntityProductOffer.php:318] ``` #### Expected behavior I suppose should be the same object as it is already loaded before flush.
admin added the BugRegression labels 2026-01-22 15:46:38 +01:00
Author
Owner

@greg0ire commented on GitHub (Aug 3, 2023):

Please learn how to format Github messages.

@greg0ire commented on GitHub (Aug 3, 2023): Please learn how to format Github messages.
Author
Owner

@derrabus commented on GitHub (Aug 3, 2023):

Please learn how to format Github messages.

I've fixed that for you. 😉

@derrabus commented on GitHub (Aug 3, 2023): > Please learn how to format Github messages. I've fixed that for you. 😉
Author
Owner

@derrabus commented on GitHub (Aug 3, 2023):

Looks like two proxies have been created for the same entity and one of the proxies is being resolved during the preFlush event in your case. This looks like an interesting edge case of your identity map corruption detection, @mpdude. Can you take a look?

@derrabus commented on GitHub (Aug 3, 2023): Looks like two proxies have been created for the same entity and one of the proxies is being resolved during the `preFlush` event in your case. This looks like an interesting edge case of your identity map corruption detection, @mpdude. Can you take a look?
Author
Owner

@tasselchof commented on GitHub (Aug 3, 2023):

Please learn how to format Github messages.

I m sorry, I was very limited in time, but I thought it’s important to create an issue because it might impact not only our software.

This is not in our code but this is something, on the first look, that os affecting doctrine itself.

I think if I will do simple test case: pull Offer from database and after this try to pull same one from proxy it will be the same problem.

@tasselchof commented on GitHub (Aug 3, 2023): > Please learn how to format Github messages. I m sorry, I was very limited in time, but I thought it’s important to create an issue because it might impact not only our software. This is not in our code but this is something, on the first look, that os affecting doctrine itself. I think if I will do simple test case: pull Offer from database and after this try to pull same one from proxy it will be the same problem.
Author
Owner

@mpdude commented on GitHub (Aug 3, 2023):

I don't fully understand how to reproduce the issue.

I am getting some values from database with native query (I am using composite keys) and generating reference from it.
[...] I am using this reference in further request and if I generate spl_object_id it's always same object. But later I am fetching one another entity from database. [...] This entity contains property productOffer with is linked with the same ids (so technically it contains same object).
And if I am checking spl_object_id($acceptanceItem->getProductOffer()) it's different object.

I have tried to put that into #10873, but that's OK so far.

So, what is the missing piece?

@mpdude commented on GitHub (Aug 3, 2023): I don't fully understand how to reproduce the issue. > I am getting some values from database with native query (I am using composite keys) and generating reference from it. > [...] I am using this reference in further request and if I generate spl_object_id it's always same object. But later I am fetching one another entity from database. [...] This entity contains property productOffer with is linked with the same ids (so technically it contains same object). > And if I am checking spl_object_id($acceptanceItem->getProductOffer()) it's different object. I have tried to put that into #10873, but that's OK so far. So, what is the missing piece?
Author
Owner

@tasselchof commented on GitHub (Aug 3, 2023):

@mpdude I tried to play a bit with reproducing an issue and rebuild annotations to be closer to live code.

The problem is that this part of the code is set to refactoring (it's very old and bad done), but it's causing this issue and I want to try to figure out why. There is 3 blocks in one function. First block writing location logs, second block updating barcodes of the offer and third is updating quantity in item. If I comment block 1 or 2 everything is fine. They just can't work together.

Adding this reference in any part of the code causing this exception as well.

@tasselchof commented on GitHub (Aug 3, 2023): @mpdude I tried to play a bit with reproducing an issue and rebuild annotations to be closer to live code. The problem is that this part of the code is set to refactoring (it's very old and bad done), but it's causing this issue and I want to try to figure out why. There is 3 blocks in one function. First block writing location logs, second block updating barcodes of the offer and third is updating quantity in item. If I comment block 1 or 2 everything is fine. They just can't work together. Adding this reference in any part of the code causing this exception as well.
Author
Owner

@tasselchof commented on GitHub (Aug 4, 2023):

There is something here... I found another case in the code. Worked like that: there is a part of logic that do some modifications and flushes the result. After flush getting reference causing this exception. And I broke down this to one case: I have order that has collection of orderProducts, each of those has a reference to Offer. So If I do like that:

echo $order->getOrderProducts()->count();

$productOffer = $this->getObjectManager()->getReference(
                    Offer::class,
                    [
                        'shop' => $shop,
                        'id'   => $result['productoffer_id']
                    ]
                );

                if ($productOffer->getType() != Offer::TYPE_BUNDLE) {
                    continue;
                }

$productOffer->getType() causing and exception ($productOffer is a proxy).

@tasselchof commented on GitHub (Aug 4, 2023): There is something here... I found another case in the code. Worked like that: there is a part of logic that do some modifications and flushes the result. After flush getting reference causing this exception. And I broke down this to one case: I have order that has collection of orderProducts, each of those has a reference to Offer. So If I do like that: ``` echo $order->getOrderProducts()->count(); $productOffer = $this->getObjectManager()->getReference( Offer::class, [ 'shop' => $shop, 'id' => $result['productoffer_id'] ] ); if ($productOffer->getType() != Offer::TYPE_BUNDLE) { continue; } ``` $productOffer->getType() causing and exception ($productOffer is a proxy).
Author
Owner

@tasselchof commented on GitHub (Aug 4, 2023):

@mpdude can you take a look? productOffer that I got from the collection is not Proxy, but reference is proxy, test is here: tests/Doctrine/Tests/ORM/Functional/Ticket/GH10868Test_2.php. Maybe it's something?

@tasselchof commented on GitHub (Aug 4, 2023): @mpdude can you take a look? productOffer that I got from the collection is not Proxy, but reference is proxy, test is here: [tests/Doctrine/Tests/ORM/Functional/Ticket/GH10868Test_2.php](https://github.com/tasselchof/doctrine2-orm/commit/54bb42e71c0009d28b555b5c60a9dc4d7cad0a77). Maybe it's something?
Author
Owner

@mpdude commented on GitHub (Aug 5, 2023):

@tasselchof #10873 contains the code as you suggest.

$reference is a reference to an instance of GH10868Offer, whereas $order->orderProducts->first() is an instance of GH10868OrderProduct.

To me, this seems logically wrong, so no surprise the test is failing.

@mpdude commented on GitHub (Aug 5, 2023): @tasselchof #10873 contains the code as you suggest. `$reference` is a reference to an instance of `GH10868Offer`, whereas `$order->orderProducts->first()` is an instance of `GH10868OrderProduct`. To me, this seems logically wrong, so no surprise the test is failing.
Author
Owner

@tasselchof commented on GitHub (Aug 5, 2023):

@mpdude no, there is nothing wrong in logic, check the assertion:

self::assertSame($reference, $orderProductFromOrder->productOffer);

I am taking first order product and comparing reference and productOffer of first orderProduct. They should be the same. But they are not.

@tasselchof commented on GitHub (Aug 5, 2023): @mpdude no, there is nothing wrong in logic, check the assertion: ` self::assertSame($reference, $orderProductFromOrder->productOffer); ` I am taking first order product and comparing reference and productOffer of first orderProduct. They should be the same. But they are not.
Author
Owner

@mpdude commented on GitHub (Aug 5, 2023):

@tasselchof Let's continue the discussion in #10873, so we're closer to the code and can make sure we're on the same page.

@mpdude commented on GitHub (Aug 5, 2023): @tasselchof Let's continue the discussion in #10873, so we're closer to the code and can make sure we're on the same page.
Author
Owner

@tasselchof commented on GitHub (Aug 5, 2023):

@mpdude I've found how this is happening (spoiler: I've updated test in it passing well). I have in Shop entity __toString magic method where I am returning "name" of the shop (if it is set). So when UnitOfWork here: a616914887/lib/Doctrine/ORM/UnitOfWork.php (L1709-L1724) trying to get key (in my case composite key) it is getting something like "123 Store" instead of "123 1" like it should. Such a weird id is missing in the identity map and this is leading to attempt to register it once more.

And removing magic method causing this:

Error: Object of class DoctrineORMModule\Proxy\__CG__\O\Entity\Shop could not be converted to string in file /home/dev/domains/alpha/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 1697
Stack trace:
  1. Error->() /home/dev/domains/alpha/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1697

So this is a specific issue for an entity, that has a composite key and one of the composite key components is an object.

@tasselchof commented on GitHub (Aug 5, 2023): @mpdude I've found how this is happening (spoiler: I've updated test in it passing well). I have in Shop entity __toString magic method where I am returning "name" of the shop (if it is set). So when UnitOfWork here: https://github.com/doctrine/orm/blob/a616914887ea160db4158d2c67752e99624f7c8a/lib/Doctrine/ORM/UnitOfWork.php#L1709-L1724 trying to get key (in my case composite key) it is getting something like "123 Store" instead of "123 1" like it should. Such a weird id is missing in the identity map and this is leading to attempt to register it once more. And removing magic method causing this: ``` Error: Object of class DoctrineORMModule\Proxy\__CG__\O\Entity\Shop could not be converted to string in file /home/dev/domains/alpha/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 1697 Stack trace: 1. Error->() /home/dev/domains/alpha/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1697 ``` So this is a specific issue for an entity, that has a composite key and one of the composite key components is an object.
Author
Owner

@conradfr commented on GitHub (Aug 8, 2023):

FWIW I get this error when I try to create an entity in a submit form in Symfony where I create the object before and set the id (manual id).

The code has not changed since "composer upgrade" and was working before.

Downgrading to doctrine/orm 1.15.5 did the trick.

Something like

        $greatEntity = new GreatEntity();
        $greatEntity->setId($id);

        $form = $this->createForm(GreatEntityType::class, $greatEntity);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $em->persist($form->getData());
            $em->flush();
        }
@conradfr commented on GitHub (Aug 8, 2023): FWIW I get this error when I try to create an entity in a submit form in Symfony where I create the object before and set the id (manual id). The code has not changed since "composer upgrade" and was working before. Downgrading to doctrine/orm 1.15.5 did the trick. Something like ``` $greatEntity = new GreatEntity(); $greatEntity->setId($id); $form = $this->createForm(GreatEntityType::class, $greatEntity); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $em->persist($form->getData()); $em->flush(); } ```
Author
Owner

@mpdude commented on GitHub (Aug 9, 2023):

@conradfr the error message may be the same, but since you’re using application-provided IDs it seems to me this is a slightly different case.

Please open a dedicated issue (or better, PR) and provide a test case to reproduce. Have a look at the GHxxxTest.php functional test cases in this repo, that should help you get going. I don’t think (at this time) Symfony has anything to do with it. Leave those parts (form handling etc) out.

@mpdude commented on GitHub (Aug 9, 2023): @conradfr the error message may be the same, but since you’re using application-provided IDs it seems to me this is a slightly different case. Please open a dedicated issue (or better, PR) and provide a test case to reproduce. Have a look at the `GHxxxTest.php` functional test cases in this repo, that should help you get going. I don’t think (at this time) Symfony has anything to do with it. Leave those parts (form handling etc) out.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7196