[DDC-1766] Initial implementation of hydration cache.
This commit is contained in:
parent
5b18718b92
commit
3047c4b955
@ -21,7 +21,8 @@ namespace Doctrine\ORM;
|
|||||||
|
|
||||||
use Doctrine\DBAL\Types\Type,
|
use Doctrine\DBAL\Types\Type,
|
||||||
Doctrine\ORM\Query\QueryException,
|
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.
|
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||||
@ -101,6 +102,11 @@ abstract class AbstractQuery
|
|||||||
*/
|
*/
|
||||||
protected $_expireResultCache = false;
|
protected $_expireResultCache = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
|
||||||
|
*/
|
||||||
|
protected $_hydrationCacheProfile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
|
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
|
||||||
*
|
*
|
||||||
@ -299,6 +305,26 @@ abstract class AbstractQuery
|
|||||||
return $this;
|
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.
|
* 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);
|
$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();
|
$stmt = $this->_doExecute();
|
||||||
|
|
||||||
if (is_numeric($stmt)) {
|
if (is_numeric($stmt)) {
|
||||||
|
$saveCache($stmt);
|
||||||
return $stmt;
|
return $stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
$data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
|
||||||
$stmt, $this->_resultSetMapping, $this->_hints
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
72
tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php
Normal file
72
tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
namespace Doctrine\Tests\ORM\Functional;
|
||||||
|
|
||||||
|
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||||
|
use Doctrine\Tests\Models\Cms\CmsUser;
|
||||||
|
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||||
|
use Doctrine\Common\Cache\ArrayCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group DDC-1766
|
||||||
|
*/
|
||||||
|
class HydrationCacheTest extends OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user