mirror of
https://github.com/doctrine/orm.git
synced 2026-03-23 22:42:18 +01:00
fetch join using composite key on join entity fails to hydrate correctly #5083
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @jakestay on GitHub (Apr 7, 2016).
Originally assigned to: @Ocramius on GitHub.
When doing a fetch join to hydrate an entity, using a join table with a composite key and no other defined fields, the hydration incorrectly creates duplicate associated entities.
This issue may possibly be related to #2548. I cannot find any more recent related bug reports.
Steps to reproduce:
Create these three entities and their corresponding database tables:
Populate the database tables with this data:
Use this code to hydrate a
Driverentity and display its contents:Expected output:
John Doe:
BMW 7 Series
Crysler 300
Dodge Dart
Mercedes C-Class
Volvo XC90
Actual output:
John Doe:
BMW 7 Series
Dodge Dart
Dodge Dart
Volvo XC90
Volvo XC90
There is a strange duplication going on here with the associated entities. Note that the problem disappears if there are additional fields defined in the join entity (
DriverRide), or if all the associated entities (Car) have already been previously loaded from the database.@jakestay commented on GitHub (Apr 12, 2016):
I think the main cause of this bug is located in the
ObjectHydratorclass. In thehydrateRowData()method,$resultPointersis used by to store a reference to the most recently hydrated object of each entity type. When a child entity needs to be linked to its parent, this method looks in$resultPointersto find a reference to the parent and, if it finds one, links the child to that parent.The problem is that
$resultPointersis an instance/object (rather than local/method) variable that does not get reinitialized every timehydrateRowData()is called, and so it may retain a reference to an entity that was hydrated the previous time the method was called rather than the current time.In this particular example, the
Carentity is hydrated before theDriverRideentity each timehydrateRowData()is called. When the method looks for the parent of theCarentity, it finds nothing the first time (becauseDriverRidehas not yet been processed at all) and, on every subsequent call, finds a reference to theDriverRideobject that was hydrated the previous time the method was called (becauseDriverRidehas not yet been processed for the current row, and$resultPointersstill retains a reference to the result of processing for the previous row).The bug disappears when additional fields are added to
DriverRideonly because doing so happens to cause theDriverRideentity to be processed before theCarentity inhydrateRowData(). The duplication of records happens because some other weird part of this method causes the child entity to be identified as not fetch joined (and so lazy loaded) every other time the method is called (not counting the first time), and so those times (the third, fifth, etc.) the link between child and parent happens to turn out correctly.I believe that the fundamental problem is that
$resultPointersis neither a local variable nor reinitialized each timehydrateRowData()is called. I cannot think of any scenario in which you would need a reference to an object that was hydrated with data from the previous row of data, so I would recommend simply reinitializing this variable at the beginning of this method. That should fix the bug.@Ocramius commented on GitHub (Sep 7, 2016):
Fixed in #5975
@sobeslavsky commented on GitHub (Aug 21, 2017):
An issue remains in 2.5.10. When using array hydration, the last joined entity in result set is not hydrated.
I'm having the following entity:
Now I call this code:
And get this result:
Notice that the last skill in result set is not hydrated. When I use scalar hydration, all fields are returned correctly. When I add an additional field to JnChallengeSkill (as @jakestay did above), the last result gets hydrated properly as well.