Second level caching with fetch joins fails #6001

Open
opened 2026-01-22 15:24:33 +01:00 by admin · 0 comments
Owner

Originally created by @githoober on GitHub (Jun 28, 2018).

Bug Report

Q A
BC Break no
Version 2.6.x

Summary

When caching a query with fetch joins using L2, its execution fails

Current behavior

The execution fails with a parameter type mismatch in \Doctrine\ORM\UnitOfWork::getEntityIdentifier

2018-06-27T19:54:32-05:00 WARN (4): PHP Error [2]: 'spl_object_hash() expects parameter 1 to be object, array given'
Memory used: 54.3Mb/54.3Mb
/opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2995
Call stack:
#0: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(line 2995),function: spl_object_hash(Array[1])
#1: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 352), class: Doctrine\ORM\UnitOfWork; function: getEntityIdentifier(Array[1])
#2: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 327), class: Doctrine\ORM\Cache\DefaultQueryCache; function: storeAssociationCache(Object:Doctrine\ORM\Cache\QueryCacheKey, Array[20], Array[1])
#3: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 1003), class: Doctrine\ORM\Cache\DefaultQueryCache; function: put(Object:Doctrine\ORM\Cache\QueryCacheKey, Object:Doctrine\ORM\Query\ResultSetMapping, Array[1], Array[0])
#4: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 909), class: Doctrine\ORM\AbstractQuery; function: executeUsingQueryCache(null, "1")
#5: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 716), class: Doctrine\ORM\AbstractQuery; function: execute(null, "1")
#6: /opt/project/common/tests6.php(line 34), class: Doctrine\ORM\AbstractQuery; function: getResult()----------------------------------------------------------------------------------------------------
2018-06-27T19:54:32-05:00 NOTICE (5): PHP Error [8]: 'Undefined index: '
Memory used: 54.3Mb/54.3Mb
/opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2995
Call stack:
#0: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 352), class: Doctrine\ORM\UnitOfWork; function: getEntityIdentifier(Array[1])
#1: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 327), class: Doctrine\ORM\Cache\DefaultQueryCache; function: storeAssociationCache(Object:Doctrine\ORM\Cache\QueryCacheKey, Array[20], Array[1])
#2: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 1003), class: Doctrine\ORM\Cache\DefaultQueryCache; function: put(Object:Doctrine\ORM\Cache\QueryCacheKey, Object:Doctrine\ORM\Query\ResultSetMapping, Array[1], Array[0])
#3: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 909), class: Doctrine\ORM\AbstractQuery; function: executeUsingQueryCache(null, "1")
#4: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 716), class: Doctrine\ORM\AbstractQuery; function: execute(null, "1")
#5: /opt/project/common/tests6.php(line 34), class: Doctrine\ORM\AbstractQuery; function: getResult()PHP Fatal error:  Uncaught TypeError: Argument 2 passed to Doctrine\ORM\Cache\EntityCacheKey::__construct() must be of the type array, null given, called in /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php on line 353 and defined in /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php:49
Stack trace:
#0 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(353): Doctrine\ORM\Cache\EntityCacheKey->__construct('Entity\\O...', NULL)
#1 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(327): Doctrine\ORM\Cache\DefaultQueryCache->storeAssociationCache(Object(Doctrine\ORM\Cache\QueryCacheKey), Array, Array)
#2 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1003): Doctrine\ORM\Cache\DefaultQueryCache->put(Object(Doctrine\ORM\Cache\QueryCacheKey), Object(Doctrine\ORM\Query\ResultSetMapping), Array, Array)
#3 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php( in /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php on line 49

How to reproduce

Use the following entities, DDL, and the query:

namespace Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="root")
 * @ORM\Entity()
 */
class RootEntity
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(name="id", type="integer")
     */
    private $id;

    /**
     * @var Collection
     *
     * @ORM\OneToMany(targetEntity="ChildEntity", mappedBy="Root", fetch="EXTRA_LAZY")
     */
    private $Children;
}
<?php
namespace Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping;

/**
 * @Mapping\Table(name="child")
 * @Mapping\Entity()
 */
class ChildEntity
{
    /**
     * @Mapping\Id
     * @Mapping\GeneratedValue(strategy="AUTO")
     * @Mapping\Column(name="id", type="integer")
     */
    private $id;

    /**
     * Values
     *
     * @var Collection
     *
     * @Mapping\ManyToMany(
     *      targetEntity="ValueEntity",
     *      inversedBy="Children",
     *      fetch="EXTRA_LAZY"
     * )
     * @Mapping\JoinTable(name="child_value",
     *   joinColumns={
     *     @Mapping\JoinColumn(name="child_id", referencedColumnName="id")
     *   },
     *   inverseJoinColumns={
     *     @Mapping\JoinColumn(name="value_id", referencedColumnName="id")
     *   }
     * )
     */
    private $Values;

    /**
     * @Mapping\ManyToOne(targetEntity="RootEntity", inversedBy="Values")
     * @Mapping\JoinColumn(name="root_id", referencedColumnName="id")
 */
    private $Root;

}
<?php

namespace Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping;

/**
 * @Mapping\Table(name="value")
 * @Mapping\Entity()
 */
class ValueEntity
{
    /**
     * @Mapping\Id
     * @Mapping\GeneratedValue(strategy="AUTO")
     * @Mapping\Column(name="id", type="integer")
     */
    private $id;

    /**
     * @var OwnerEntity
     *
     * @Mapping\ManyToOne(targetEntity="OwnerEntity", inversedBy="Values")
     * @Mapping\JoinColumn(name="id", referencedColumnName="id")
     */
    private $Owner;

    /**
     * Children
     *
     * @var Collection
     *
     * @Mapping\ManyToMany(targetEntity="ChildEntity", mappedBy="Values")
     */
    private $Children;

}
<?php

namespace Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping;

/**
 * @Mapping\Table(name="owner")
 * @Mapping\Entity()
 */
class OwnerEntity
{
    /**
     * @Mapping\Id
     * @Mapping\GeneratedValue(strategy="AUTO")
     * @Mapping\Column(name="id", type="integer")
     */
    private $id;

    /**
     * Children
     *
     * @var Collection
     *
     *  @Mapping\OneToMany(
     *      targetEntity="ValueEntity",
     *      mappedBy="Owner",
     *      fetch="EXTRA_LAZY"
     * )
 */
    private $Values;

}
CREATE TABLE root
(
    id int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT
);
INSERT INTO root (id) VALUES (1);

CREATE TABLE child
(
    id int(11) PRIMARY KEY NOT NULL,
    root_id int(11),
    CONSTRAINT child_root_id_fk FOREIGN KEY (root_id) REFERENCES root (id)
);
CREATE INDEX child_root_id_fk ON child (root_id);
INSERT INTO child (id, root_id) VALUES (1, 1);
INSERT INTO child (id, root_id) VALUES (2, 1);

CREATE TABLE owner
(
    id int(11) PRIMARY KEY NOT NULL
);
INSERT INTO owner (id) VALUES (1);

CREATE TABLE value
(
    id int(11) PRIMARY KEY NOT NULL,
    owner_id int(11),
    CONSTRAINT value_owner_id_fk FOREIGN KEY (owner_id) REFERENCES owner (id)
);
CREATE INDEX value_owner_id_fk ON value (owner_id);
INSERT INTO value (id, owner_id) VALUES (1, 1);
INSERT INTO value (id, owner_id) VALUES (2, 1);

CREATE TABLE child_value
(
    child_id int(11) NOT NULL,
    value_id int(11) NOT NULL,
    CONSTRAINT child_value_child_id_fk FOREIGN KEY (child_id) REFERENCES child (id),
    CONSTRAINT child_value_value_id_fk FOREIGN KEY (value_id) REFERENCES value (id)
);
CREATE INDEX child_value_child_id_fk ON child_value (child_id);
CREATE INDEX child_value_value_id_fk ON child_value (value_id);
INSERT INTO child_value (child_id, value_id) VALUES (1, 1);
INSERT INTO child_value (child_id, value_id) VALUES (2, 2);
$dql = "
    SELECT Root, Child, Value, Owner
    FROM Entity\RootEntity Root
    LEFT JOIN Root.Children Child
    LEFT JOIN Child.Values Value
    LEFT JOIN Value.Owner Owner
    WHERE Root.id = 1
    ";


$query = $em->createQuery($dql);

$query->setCacheable(true);

$root = $query->getResult();

Expected behavior

The execution does not break, and the query result is cached

When Owner is removed from the SELECT statement, the query finishes successfully

Originally created by @githoober on GitHub (Jun 28, 2018). ### Bug Report | Q | A |------------ | ------ | BC Break | no | Version | 2.6.x #### Summary When caching a query with fetch joins using L2, its execution fails #### Current behavior The execution fails with a parameter type mismatch in \Doctrine\ORM\UnitOfWork::getEntityIdentifier ``` 2018-06-27T19:54:32-05:00 WARN (4): PHP Error [2]: 'spl_object_hash() expects parameter 1 to be object, array given' Memory used: 54.3Mb/54.3Mb /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2995 Call stack: #0: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(line 2995),function: spl_object_hash(Array[1]) #1: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 352), class: Doctrine\ORM\UnitOfWork; function: getEntityIdentifier(Array[1]) #2: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 327), class: Doctrine\ORM\Cache\DefaultQueryCache; function: storeAssociationCache(Object:Doctrine\ORM\Cache\QueryCacheKey, Array[20], Array[1]) #3: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 1003), class: Doctrine\ORM\Cache\DefaultQueryCache; function: put(Object:Doctrine\ORM\Cache\QueryCacheKey, Object:Doctrine\ORM\Query\ResultSetMapping, Array[1], Array[0]) #4: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 909), class: Doctrine\ORM\AbstractQuery; function: executeUsingQueryCache(null, "1") #5: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 716), class: Doctrine\ORM\AbstractQuery; function: execute(null, "1") #6: /opt/project/common/tests6.php(line 34), class: Doctrine\ORM\AbstractQuery; function: getResult()---------------------------------------------------------------------------------------------------- 2018-06-27T19:54:32-05:00 NOTICE (5): PHP Error [8]: 'Undefined index: ' Memory used: 54.3Mb/54.3Mb /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2995 Call stack: #0: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 352), class: Doctrine\ORM\UnitOfWork; function: getEntityIdentifier(Array[1]) #1: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(line 327), class: Doctrine\ORM\Cache\DefaultQueryCache; function: storeAssociationCache(Object:Doctrine\ORM\Cache\QueryCacheKey, Array[20], Array[1]) #2: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 1003), class: Doctrine\ORM\Cache\DefaultQueryCache; function: put(Object:Doctrine\ORM\Cache\QueryCacheKey, Object:Doctrine\ORM\Query\ResultSetMapping, Array[1], Array[0]) #3: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 909), class: Doctrine\ORM\AbstractQuery; function: executeUsingQueryCache(null, "1") #4: /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(line 716), class: Doctrine\ORM\AbstractQuery; function: execute(null, "1") #5: /opt/project/common/tests6.php(line 34), class: Doctrine\ORM\AbstractQuery; function: getResult()PHP Fatal error: Uncaught TypeError: Argument 2 passed to Doctrine\ORM\Cache\EntityCacheKey::__construct() must be of the type array, null given, called in /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php on line 353 and defined in /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php:49 Stack trace: #0 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(353): Doctrine\ORM\Cache\EntityCacheKey->__construct('Entity\\O...', NULL) #1 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php(327): Doctrine\ORM\Cache\DefaultQueryCache->storeAssociationCache(Object(Doctrine\ORM\Cache\QueryCacheKey), Array, Array) #2 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1003): Doctrine\ORM\Cache\DefaultQueryCache->put(Object(Doctrine\ORM\Cache\QueryCacheKey), Object(Doctrine\ORM\Query\ResultSetMapping), Array, Array) #3 /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php( in /opt/project/common/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php on line 49 ``` #### How to reproduce Use the following entities, DDL, and the query: ```<?php namespace Entity; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Table(name="root") * @ORM\Entity() */ class RootEntity { /** * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") * @ORM\Column(name="id", type="integer") */ private $id; /** * @var Collection * * @ORM\OneToMany(targetEntity="ChildEntity", mappedBy="Root", fetch="EXTRA_LAZY") */ private $Children; } ``` ``` <?php namespace Entity; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping; /** * @Mapping\Table(name="child") * @Mapping\Entity() */ class ChildEntity { /** * @Mapping\Id * @Mapping\GeneratedValue(strategy="AUTO") * @Mapping\Column(name="id", type="integer") */ private $id; /** * Values * * @var Collection * * @Mapping\ManyToMany( * targetEntity="ValueEntity", * inversedBy="Children", * fetch="EXTRA_LAZY" * ) * @Mapping\JoinTable(name="child_value", * joinColumns={ * @Mapping\JoinColumn(name="child_id", referencedColumnName="id") * }, * inverseJoinColumns={ * @Mapping\JoinColumn(name="value_id", referencedColumnName="id") * } * ) */ private $Values; /** * @Mapping\ManyToOne(targetEntity="RootEntity", inversedBy="Values") * @Mapping\JoinColumn(name="root_id", referencedColumnName="id") */ private $Root; } ``` ``` <?php namespace Entity; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping; /** * @Mapping\Table(name="value") * @Mapping\Entity() */ class ValueEntity { /** * @Mapping\Id * @Mapping\GeneratedValue(strategy="AUTO") * @Mapping\Column(name="id", type="integer") */ private $id; /** * @var OwnerEntity * * @Mapping\ManyToOne(targetEntity="OwnerEntity", inversedBy="Values") * @Mapping\JoinColumn(name="id", referencedColumnName="id") */ private $Owner; /** * Children * * @var Collection * * @Mapping\ManyToMany(targetEntity="ChildEntity", mappedBy="Values") */ private $Children; } ``` ``` <?php namespace Entity; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping; /** * @Mapping\Table(name="owner") * @Mapping\Entity() */ class OwnerEntity { /** * @Mapping\Id * @Mapping\GeneratedValue(strategy="AUTO") * @Mapping\Column(name="id", type="integer") */ private $id; /** * Children * * @var Collection * * @Mapping\OneToMany( * targetEntity="ValueEntity", * mappedBy="Owner", * fetch="EXTRA_LAZY" * ) */ private $Values; } ``` ``` CREATE TABLE root ( id int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT ); INSERT INTO root (id) VALUES (1); CREATE TABLE child ( id int(11) PRIMARY KEY NOT NULL, root_id int(11), CONSTRAINT child_root_id_fk FOREIGN KEY (root_id) REFERENCES root (id) ); CREATE INDEX child_root_id_fk ON child (root_id); INSERT INTO child (id, root_id) VALUES (1, 1); INSERT INTO child (id, root_id) VALUES (2, 1); CREATE TABLE owner ( id int(11) PRIMARY KEY NOT NULL ); INSERT INTO owner (id) VALUES (1); CREATE TABLE value ( id int(11) PRIMARY KEY NOT NULL, owner_id int(11), CONSTRAINT value_owner_id_fk FOREIGN KEY (owner_id) REFERENCES owner (id) ); CREATE INDEX value_owner_id_fk ON value (owner_id); INSERT INTO value (id, owner_id) VALUES (1, 1); INSERT INTO value (id, owner_id) VALUES (2, 1); CREATE TABLE child_value ( child_id int(11) NOT NULL, value_id int(11) NOT NULL, CONSTRAINT child_value_child_id_fk FOREIGN KEY (child_id) REFERENCES child (id), CONSTRAINT child_value_value_id_fk FOREIGN KEY (value_id) REFERENCES value (id) ); CREATE INDEX child_value_child_id_fk ON child_value (child_id); CREATE INDEX child_value_value_id_fk ON child_value (value_id); INSERT INTO child_value (child_id, value_id) VALUES (1, 1); INSERT INTO child_value (child_id, value_id) VALUES (2, 2); ``` ``` $dql = " SELECT Root, Child, Value, Owner FROM Entity\RootEntity Root LEFT JOIN Root.Children Child LEFT JOIN Child.Values Value LEFT JOIN Value.Owner Owner WHERE Root.id = 1 "; $query = $em->createQuery($dql); $query->setCacheable(true); $root = $query->getResult(); ``` #### Expected behavior The execution does not break, and the query result is cached When `Owner` is removed from the SELECT statement, the query finishes successfully
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6001