Merge pull request #611 from stefankleff/fix-eagerloading
Fixed typo in hints. Caused slow loading of eager entities.
This commit is contained in:
commit
52b2e066c5
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
namespace Doctrine\ORM\Internal\Hydration;
|
namespace Doctrine\ORM\Internal\Hydration;
|
||||||
|
|
||||||
|
use Doctrine\ORM\UnitOfWork;
|
||||||
use PDO;
|
use PDO;
|
||||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
use Doctrine\ORM\PersistentCollection;
|
use Doctrine\ORM\PersistentCollection;
|
||||||
@ -94,8 +95,8 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
|
|
||||||
$this->resultCounter = 0;
|
$this->resultCounter = 0;
|
||||||
|
|
||||||
if ( ! isset($this->_hints['deferEagerLoad'])) {
|
if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
|
||||||
$this->_hints['deferEagerLoad'] = true;
|
$this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||||
@ -152,7 +153,7 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
*/
|
*/
|
||||||
protected function cleanup()
|
protected function cleanup()
|
||||||
{
|
{
|
||||||
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
|
$eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true;
|
||||||
|
|
||||||
parent::cleanup();
|
parent::cleanup();
|
||||||
|
|
||||||
|
@ -852,7 +852,7 @@ class BasicEntityPersister
|
|||||||
$stmt = $this->conn->executeQuery($query, $params, $types);
|
$stmt = $this->conn->executeQuery($query, $params, $types);
|
||||||
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
||||||
|
|
||||||
return $hydrator->hydrateAll($stmt, $this->rsm, array('deferEagerLoads' => true));
|
return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -909,7 +909,7 @@ class BasicEntityPersister
|
|||||||
|
|
||||||
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
||||||
|
|
||||||
return $hydrator->hydrateAll($stmt, $this->rsm, array('deferEagerLoads' => true));
|
return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -940,7 +940,7 @@ class BasicEntityPersister
|
|||||||
private function loadArrayFromStatement($assoc, $stmt)
|
private function loadArrayFromStatement($assoc, $stmt)
|
||||||
{
|
{
|
||||||
$rsm = $this->rsm;
|
$rsm = $this->rsm;
|
||||||
$hints = array('deferEagerLoads' => true);
|
$hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true);
|
||||||
|
|
||||||
if (isset($assoc['indexBy'])) {
|
if (isset($assoc['indexBy'])) {
|
||||||
$rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed.
|
$rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed.
|
||||||
@ -963,8 +963,8 @@ class BasicEntityPersister
|
|||||||
{
|
{
|
||||||
$rsm = $this->rsm;
|
$rsm = $this->rsm;
|
||||||
$hints = array(
|
$hints = array(
|
||||||
'deferEagerLoads' => true,
|
UnitOfWork::HINT_DEFEREAGERLOAD => true,
|
||||||
'collection' => $coll
|
'collection' => $coll
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isset($assoc['indexBy'])) {
|
if (isset($assoc['indexBy'])) {
|
||||||
|
@ -76,6 +76,13 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
*/
|
*/
|
||||||
const STATE_REMOVED = 4;
|
const STATE_REMOVED = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hint used to collect all primary keys of associated entities during hydration
|
||||||
|
* and execute it in a dedicated query afterwards
|
||||||
|
* @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql
|
||||||
|
*/
|
||||||
|
const HINT_DEFEREAGERLOAD = 'deferEagerLoad';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The identity map that holds references to all managed entities that have
|
* The identity map that holds references to all managed entities that have
|
||||||
* an identity. The entities are grouped by their class name.
|
* an identity. The entities are grouped by their class name.
|
||||||
@ -2616,7 +2623,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
||||||
// then we can append this entity for eager loading!
|
// then we can append this entity for eager loading!
|
||||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
||||||
isset($hints['deferEagerLoad']) &&
|
isset($hints[self::HINT_DEFEREAGERLOAD]) &&
|
||||||
!$targetClass->isIdentifierComposite &&
|
!$targetClass->isIdentifierComposite &&
|
||||||
$newValue instanceof Proxy &&
|
$newValue instanceof Proxy &&
|
||||||
$newValue->__isInitialized__ === false) {
|
$newValue->__isInitialized__ === false) {
|
||||||
@ -2641,7 +2648,7 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// Deferred eager load only works for single identifier classes
|
// Deferred eager load only works for single identifier classes
|
||||||
case (isset($hints['deferEagerLoad']) && ! $targetClass->isIdentifierComposite):
|
case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite):
|
||||||
// TODO: Is there a faster approach?
|
// TODO: Is there a faster approach?
|
||||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||||
|
|
||||||
|
109
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2346Test.php
Normal file
109
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2346Test.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||||
|
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\DBAL\Logging\DebugStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group DDC-2346
|
||||||
|
*/
|
||||||
|
class DDC2346Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Doctrine\DBAL\Logging\DebugStack
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function setup()
|
||||||
|
{
|
||||||
|
parent::setup();
|
||||||
|
|
||||||
|
$this->_schemaTool->createSchema(array(
|
||||||
|
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2346Foo'),
|
||||||
|
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2346Bar'),
|
||||||
|
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2346Baz'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->logger = new DebugStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that fetching a OneToMany association with fetch="EAGER" does not cause N+1 queries
|
||||||
|
*/
|
||||||
|
public function testIssue()
|
||||||
|
{
|
||||||
|
$foo1 = new DDC2346Foo();
|
||||||
|
$foo2 = new DDC2346Foo();
|
||||||
|
|
||||||
|
$baz1 = new DDC2346Baz();
|
||||||
|
$baz2 = new DDC2346Baz();
|
||||||
|
|
||||||
|
$baz1->foo = $foo1;
|
||||||
|
$baz2->foo = $foo2;
|
||||||
|
|
||||||
|
$foo1->bars[] = $baz1;
|
||||||
|
$foo1->bars[] = $baz2;
|
||||||
|
|
||||||
|
$this->_em->persist($foo1);
|
||||||
|
$this->_em->persist($foo2);
|
||||||
|
$this->_em->persist($baz1);
|
||||||
|
$this->_em->persist($baz2);
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$this->_em->getConnection()->getConfiguration()->setSQLLogger($this->logger);
|
||||||
|
|
||||||
|
$fetchedBazs = $this->_em->getRepository(__NAMESPACE__ . '\\DDC2346Baz')->findAll();
|
||||||
|
|
||||||
|
$this->assertCount(2, $fetchedBazs);
|
||||||
|
$this->assertCount(2, $this->logger->queries, 'The total number of executed queries is 2, and not n+1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @Entity */
|
||||||
|
class DDC2346Foo
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue */
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DDC2346Bar[]|\Doctrine\Common\Collections\Collection
|
||||||
|
*
|
||||||
|
* @OneToMany(targetEntity="DDC2346Bar", mappedBy="foo")
|
||||||
|
*/
|
||||||
|
public $bars;
|
||||||
|
|
||||||
|
/** Constructor */
|
||||||
|
public function __construct() {
|
||||||
|
$this->bars = new ArrayCollection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
* @InheritanceType("JOINED")
|
||||||
|
* @DiscriminatorColumn(name="discr", type="string")
|
||||||
|
* @DiscriminatorMap({"baz" = "DDC2346Baz"})
|
||||||
|
*/
|
||||||
|
class DDC2346Bar
|
||||||
|
{
|
||||||
|
/** @Id @Column(type="integer") @GeneratedValue */
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/** @ManyToOne(targetEntity="DDC2346Foo", inversedBy="bars", fetch="EAGER") */
|
||||||
|
public $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class DDC2346Baz extends DDC2346Bar
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user