From 3047c4b955c834860ca3f5b461e42cc0c2397d38 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Apr 2012 23:10:30 +0200 Subject: [PATCH 1/9] [DDC-1766] Initial implementation of hydration cache. --- lib/Doctrine/ORM/AbstractQuery.php | 87 ++++++++++++++++++- .../ORM/Functional/HydrationCacheTest.php | 72 +++++++++++++++ 2 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 4ada9160e..be22ad895 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -21,7 +21,8 @@ namespace Doctrine\ORM; use Doctrine\DBAL\Types\Type, Doctrine\ORM\Query\QueryException, - Doctrine\DBAL\Cache\QueryCacheProfile; + Doctrine\DBAL\Cache\QueryCacheProfile, + Doctrine\ORM\Internal\Hydration\CacheHydrator; /** * Base contract for ORM queries. Base class for Query and NativeQuery. @@ -101,6 +102,11 @@ abstract class AbstractQuery */ protected $_expireResultCache = false; + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_hydrationCacheProfile; + /** * Initializes a new instance of a class derived from AbstractQuery. * @@ -299,6 +305,26 @@ abstract class AbstractQuery return $this; } + /** + * Set a cache profile for hydration caching. + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * @return \Doctrine\ORM\AbstractQuery + */ + public function setHydrationCacheProfile(QueryCacheProfile $profile = null) + { + $this->_hydrationCacheProfile = $profile; + return $this; + } + + /** + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function getHydrationCacheProfile() + { + return $this->_hydrationCacheProfile; + } + /** * Defines a cache driver to be used for caching result sets and implictly enables caching. * @@ -644,15 +670,72 @@ abstract class AbstractQuery $this->setParameters($params); } + $saveCache = function() {}; + if ($this->_hydrationCacheProfile !== null) { + list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); + + $qcp = $this->getHydrationCacheProfile(); + $cache = $qcp->getResultCacheDriver(); + + $result = $cache->fetch($cacheKey); + if (isset($result[$realCacheKey])) { + return $result[$realCacheKey]; + } + + if ( ! $result) { + $result = array(); + } + $saveCache = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $qcp) { + $result[$realCacheKey] = $data; + $cache->save($cacheKey, $result, $qcp->getLifetime()); + }; + } + $stmt = $this->_doExecute(); if (is_numeric($stmt)) { + $saveCache($stmt); return $stmt; } - return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( + $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( $stmt, $this->_resultSetMapping, $this->_hints ); + + $saveCache($data); + + return $data; + } + + /** + * Get the result cache id to use to store the result set cache entry. + * Will return the configured id if it exists otherwise a hash will be + * automatically generated for you. + * + * @return array ($key, $hash) + */ + protected function getHydrationCacheId() + { + $params = $this->getParameters(); + foreach ($params AS $key => $value) { + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { + if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) { + $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $class = $this->_em->getClassMetadata(get_class($value)); + $idValues = $class->getIdentifierValues($value); + } + $params[$key] = $idValues; + } + } + + $sql = $this->getSQL(); + $hints = $this->getHints(); + $hints['hydrationMode'] = $this->getHydrationMode(); + ksort($hints); + + $qcp = $this->getHydrationCacheProfile(); + return $qcp->generateCacheKeys($sql, $params, $hints); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php new file mode 100644 index 000000000..9f3acf8c6 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php @@ -0,0 +1,72 @@ +useModelSet('cms'); + parent::setUp(); + } + + public function testHydrationCache() + { + $cache = new ArrayCache(); + + $user = new CmsUser; + $user->name = "Benjamin"; + $user->username = "beberlei"; + $user->status = 'active'; + + $this->_em->persist($user); + $this->_em->flush(); + $this->_em->clear(); + + + $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) + ->getResult(); + + $c = $this->getCurrentQueryCount(); + $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) + ->getResult(); + + $this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!"); + + $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) + ->getArrayResult(); + + $this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration is part of cache key."); + + $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) + ->getArrayResult(); + + $this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration now cached"); + + $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile('cachekey', null, $cache)) + ->getArrayResult(); + + $data = $this->readAttribute($cache, 'data'); + var_dump(array_keys($data)); + + $this->assertTrue($cache->contains('cachekey'), 'Explicit cache key'); + } +} + From 306f9e0ca204d93499ec9e1b88b10a606824d973 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Apr 2012 23:21:06 +0200 Subject: [PATCH 2/9] [DDC-1766] Rename closure --- lib/Doctrine/ORM/AbstractQuery.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index be22ad895..956775b1a 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -670,7 +670,7 @@ abstract class AbstractQuery $this->setParameters($params); } - $saveCache = function() {}; + $setCacheEntry = function() {}; if ($this->_hydrationCacheProfile !== null) { list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); @@ -685,7 +685,8 @@ abstract class AbstractQuery if ( ! $result) { $result = array(); } - $saveCache = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $qcp) { + + $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $qcp) { $result[$realCacheKey] = $data; $cache->save($cacheKey, $result, $qcp->getLifetime()); }; @@ -694,7 +695,7 @@ abstract class AbstractQuery $stmt = $this->_doExecute(); if (is_numeric($stmt)) { - $saveCache($stmt); + $setCacheEntry($stmt); return $stmt; } @@ -702,7 +703,7 @@ abstract class AbstractQuery $stmt, $this->_resultSetMapping, $this->_hints ); - $saveCache($data); + $setCacheEntry($data); return $data; } From 864fbbdaaf6d9b7d5a78fccd126015dae82eeca0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Apr 2012 23:24:51 +0200 Subject: [PATCH 3/9] [DDC-1766] Remove some testcode --- .../Tests/ORM/Functional/HydrationCacheTest.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php index 9f3acf8c6..2012e1c99 100644 --- a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php @@ -57,16 +57,6 @@ class HydrationCacheTest extends OrmFunctionalTestCase ->getArrayResult(); $this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration now cached"); - - $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; - $users = $this->_em->createQuery($dql) - ->setHydrationCacheProfile(new QueryCacheProfile('cachekey', null, $cache)) - ->getArrayResult(); - - $data = $this->readAttribute($cache, 'data'); - var_dump(array_keys($data)); - - $this->assertTrue($cache->contains('cachekey'), 'Explicit cache key'); } } From fd2a22bd56cf5be346f839c4f60f798279979777 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Apr 2012 23:42:17 +0200 Subject: [PATCH 4/9] [DDC-1766] Add test with explicit cache key. --- .../Tests/ORM/Functional/HydrationCacheTest.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php index 2012e1c99..f16124706 100644 --- a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php @@ -32,31 +32,41 @@ class HydrationCacheTest extends OrmFunctionalTestCase $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; + $users = $this->_em->createQuery($dql) ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) ->getResult(); $c = $this->getCurrentQueryCount(); - $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; $users = $this->_em->createQuery($dql) ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) ->getResult(); $this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!"); - $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; $users = $this->_em->createQuery($dql) ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) ->getArrayResult(); $this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration is part of cache key."); - $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; $users = $this->_em->createQuery($dql) ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)) ->getArrayResult(); $this->assertEquals($c + 1, $this->getCurrentQueryCount(), "Hydration now cached"); + + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache)) + ->getArrayResult(); + + $this->assertTrue($cache->contains('cachekey'), 'Explicit cache key'); + + $users = $this->_em->createQuery($dql) + ->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache)) + ->getArrayResult(); + $this->assertEquals($c + 2, $this->getCurrentQueryCount(), "Hydration now cached"); + } } From c32a77e6be46f823906199e9b9eb3a27d0f817e5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Apr 2012 23:47:32 +0200 Subject: [PATCH 5/9] [DDC-1766] Add usage of default result cache driver, add more docs. --- lib/Doctrine/ORM/AbstractQuery.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 956775b1a..3fba6c4cd 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -308,11 +308,24 @@ abstract class AbstractQuery /** * Set a cache profile for hydration caching. * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * @example + * $lifetime = 100; + * $resultKey = "abc"; + * $query->setHydrationCacheProfile(new QueryCacheProfile()); + * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey)); + * * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile * @return \Doctrine\ORM\AbstractQuery */ public function setHydrationCacheProfile(QueryCacheProfile $profile = null) { + if ( ! $profile->getResultCacheDriver()) { + $profile = $profile->setResultCacheDriver($this->_em->getConfiguration()->getResultCacheImpl()); + } + $this->_hydrationCacheProfile = $profile; return $this; } From d31c7f5e2b2a0bedcebbf44330dcdca9b9c794f0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 4 Apr 2012 23:55:14 +0200 Subject: [PATCH 6/9] [DDC-1766] Explain details of Hydration cache, introduce AbstractQuery#setResultCacheProfile method --- lib/Doctrine/ORM/AbstractQuery.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 3fba6c4cd..f2a20fa0f 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -311,6 +311,12 @@ abstract class AbstractQuery * If no result cache driver is set in the QueryCacheProfile, the default * result cache driver is used from the configuration. * + * Important: Hydration caching does NOT register entities in the + * UnitOfWork when retrieved from the cache. Never use result cached + * entities for requests that also flush the EntityManager. If you want + * some form of caching with UnitOfWork registration you should use + * {@see AbstractQuery::setResultCacheProfile()}. + * * @example * $lifetime = 100; * $resultKey = "abc"; @@ -338,6 +344,25 @@ abstract class AbstractQuery return $this->_hydrationCacheProfile; } + /** + * Set a cache profile for the result cache. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * @return \Doctrine\ORM\AbstractQuery + */ + public function setResultCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $profile = $profile->setResultCacheDriver($this->_em->getConfiguration()->getResultCacheImpl()); + } + + $this->_queryCacheProfile = $profile; + return $this; + } + /** * Defines a cache driver to be used for caching result sets and implictly enables caching. * From f7496b1482d208b039a0ba81c99709a3815bf1e5 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 5 Apr 2012 00:26:09 +0200 Subject: [PATCH 7/9] [DDC-1766] Cleaned up code. --- lib/Doctrine/ORM/AbstractQuery.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index f2a20fa0f..bfc80a9bb 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -329,10 +329,12 @@ abstract class AbstractQuery public function setHydrationCacheProfile(QueryCacheProfile $profile = null) { if ( ! $profile->getResultCacheDriver()) { - $profile = $profile->setResultCacheDriver($this->_em->getConfiguration()->getResultCacheImpl()); + $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); } $this->_hydrationCacheProfile = $profile; + return $this; } @@ -356,10 +358,12 @@ abstract class AbstractQuery public function setResultCacheProfile(QueryCacheProfile $profile = null) { if ( ! $profile->getResultCacheDriver()) { - $profile = $profile->setResultCacheDriver($this->_em->getConfiguration()->getResultCacheImpl()); + $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); } $this->_queryCacheProfile = $profile; + return $this; } @@ -709,13 +713,14 @@ abstract class AbstractQuery } $setCacheEntry = function() {}; + if ($this->_hydrationCacheProfile !== null) { list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); - $qcp = $this->getHydrationCacheProfile(); - $cache = $qcp->getResultCacheDriver(); - + $qcp = $this->getHydrationCacheProfile(); + $cache = $qcp->getResultCacheDriver(); $result = $cache->fetch($cacheKey); + if (isset($result[$realCacheKey])) { return $result[$realCacheKey]; } @@ -734,6 +739,7 @@ abstract class AbstractQuery if (is_numeric($stmt)) { $setCacheEntry($stmt); + return $stmt; } @@ -756,6 +762,7 @@ abstract class AbstractQuery protected function getHydrationCacheId() { $params = $this->getParameters(); + foreach ($params AS $key => $value) { if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) { From 1095fb39cbfcf923a7d77eeae40cd71706a5a889 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 5 Apr 2012 00:27:23 +0200 Subject: [PATCH 8/9] [DDC-1766] More cleanups --- lib/Doctrine/ORM/AbstractQuery.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index bfc80a9bb..7068b6439 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -768,19 +768,19 @@ abstract class AbstractQuery if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) { $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); } else { - $class = $this->_em->getClassMetadata(get_class($value)); + $class = $this->_em->getClassMetadata(get_class($value)); $idValues = $class->getIdentifierValues($value); } $params[$key] = $idValues; } } - $sql = $this->getSQL(); - $hints = $this->getHints(); + $sql = $this->getSQL(); + $hints = $this->getHints(); $hints['hydrationMode'] = $this->getHydrationMode(); + $qcp = $this->getHydrationCacheProfile(); ksort($hints); - $qcp = $this->getHydrationCacheProfile(); return $qcp->generateCacheKeys($sql, $params, $hints); } From 0b3577f2d2551f720f4cd0b80fcc9c1722e04986 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 5 Apr 2012 22:40:40 +0200 Subject: [PATCH 9/9] [DDC-1766] Rewrite getHydrationCacheId() to use existing processParameterValue() method. Other code style changes. --- lib/Doctrine/ORM/AbstractQuery.php | 29 +++++++------------ lib/Doctrine/ORM/Configuration.php | 22 ++++++++++++++ .../ORM/Functional/HydrationCacheTest.php | 26 +++++++++++++---- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 7068b6439..07af294b1 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -20,8 +20,8 @@ namespace Doctrine\ORM; use Doctrine\DBAL\Types\Type, - Doctrine\ORM\Query\QueryException, Doctrine\DBAL\Cache\QueryCacheProfile, + Doctrine\ORM\Query\QueryException, Doctrine\ORM\Internal\Hydration\CacheHydrator; /** @@ -30,7 +30,6 @@ use Doctrine\DBAL\Types\Type, * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org * @since 2.0 - * @version $Revision$ * @author Benjamin Eberlei * @author Guilherme Blanco * @author Jonathan Wage @@ -329,7 +328,7 @@ abstract class AbstractQuery public function setHydrationCacheProfile(QueryCacheProfile $profile = null) { if ( ! $profile->getResultCacheDriver()) { - $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl(); + $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl(); $profile = $profile->setResultCacheDriver($resultCacheDriver); } @@ -717,9 +716,9 @@ abstract class AbstractQuery if ($this->_hydrationCacheProfile !== null) { list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); - $qcp = $this->getHydrationCacheProfile(); - $cache = $qcp->getResultCacheDriver(); - $result = $cache->fetch($cacheKey); + $queryCacheProfile = $this->getHydrationCacheProfile(); + $cache = $queryCacheProfile->getResultCacheDriver(); + $result = $cache->fetch($cacheKey); if (isset($result[$realCacheKey])) { return $result[$realCacheKey]; @@ -729,9 +728,9 @@ abstract class AbstractQuery $result = array(); } - $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $qcp) { + $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) { $result[$realCacheKey] = $data; - $cache->save($cacheKey, $result, $qcp->getLifetime()); + $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime()); }; } @@ -764,24 +763,16 @@ abstract class AbstractQuery $params = $this->getParameters(); foreach ($params AS $key => $value) { - if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { - if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) { - $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); - } else { - $class = $this->_em->getClassMetadata(get_class($value)); - $idValues = $class->getIdentifierValues($value); - } - $params[$key] = $idValues; - } + $params[$key] = $this->processParameterValue($value); } $sql = $this->getSQL(); + $queryCacheProfile = $this->getHydrationCacheProfile(); $hints = $this->getHints(); $hints['hydrationMode'] = $this->getHydrationMode(); - $qcp = $this->getHydrationCacheProfile(); ksort($hints); - return $qcp->generateCacheKeys($sql, $params, $hints); + return $queryCacheProfile->generateCacheKeys($sql, $params, $hints); } /** diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index b0e1f0fd0..f4e473e5a 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -246,6 +246,28 @@ class Configuration extends \Doctrine\DBAL\Configuration $this->_attributes['queryCacheImpl'] = $cacheImpl; } + /** + * Gets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getHydrationCacheImpl() + { + return isset($this->_attributes['hydrationCacheImpl']) + ? $this->_attributes['hydrationCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setHydrationCacheImpl(Cache $cacheImpl) + { + $this->_attributes['hydrationCacheImpl'] = $cacheImpl; + } + /** * Gets the cache driver implementation that is used for metadata caching. * diff --git a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php index f16124706..897421164 100644 --- a/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php @@ -15,11 +15,6 @@ class HydrationCacheTest extends OrmFunctionalTestCase { $this->useModelSet('cms'); parent::setUp(); - } - - public function testHydrationCache() - { - $cache = new ArrayCache(); $user = new CmsUser; $user->name = "Benjamin"; @@ -29,8 +24,11 @@ class HydrationCacheTest extends OrmFunctionalTestCase $this->_em->persist($user); $this->_em->flush(); $this->_em->clear(); + } - + public function testHydrationCache() + { + $cache = new ArrayCache(); $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u"; $users = $this->_em->createQuery($dql) @@ -66,7 +64,23 @@ class HydrationCacheTest extends OrmFunctionalTestCase ->setHydrationCacheProfile(new QueryCacheProfile(null, 'cachekey', $cache)) ->getArrayResult(); $this->assertEquals($c + 2, $this->getCurrentQueryCount(), "Hydration now cached"); + } + public function testHydrationParametersSerialization() + { + $cache = new ArrayCache(); + $user = new CmsUser(); + $user->id = 1; + + $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1"; + $query = $this->_em->createQuery($dql) + ->setParameter(1, $user) + ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)); + + $query->getResult(); + $c = $this->getCurrentQueryCount(); + $query->getResult(); + $this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!"); } }