1
0
mirror of synced 2025-02-20 22:23:14 +03:00

DDC-217 - Add support for QueryCacheProfiles and remove the old result caching code from ORM. Deprecate a bunch of methods in favor of using the AbstractQuery#getQueryCacheProfile method.

This commit is contained in:
Benjamin Eberlei 2011-10-23 23:28:23 +02:00
parent 9b8d2d512b
commit adc4840cce
11 changed files with 95 additions and 156 deletions

View File

@ -20,7 +20,8 @@
namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\QueryException;
Doctrine\ORM\Query\QueryException,
Doctrine\DBAL\Cache\QueryCacheProfile;
/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
@ -91,34 +92,15 @@ abstract class AbstractQuery
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
* The locally set cache driver used for caching result sets of this query.
*
* @var CacheDriver
* @param \Doctrine\DBAL\Cache\QueryCacheProfile
*/
protected $_resultCacheDriver;
/**
* Boolean flag for whether or not to cache the results of this query.
*
* @var boolean
*/
protected $_useResultCache;
/**
* @var string The id to store the result cache entry under.
*/
protected $_resultCacheId;
protected $_queryCacheProfile;
/**
* @var boolean Boolean value that indicates whether or not expire the result cache.
*/
protected $_expireResultCache = false;
/**
* @var int Result Cache lifetime.
*/
protected $_resultCacheTTL;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
@ -260,7 +242,7 @@ abstract class AbstractQuery
}
/**
* Defines a cache driver to be used for caching result sets.
* Defines a cache driver to be used for caching result sets and implictly enables caching.
*
* @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery
@ -270,9 +252,10 @@ abstract class AbstractQuery
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
$this->_resultCacheDriver = $resultCacheDriver;
if ($resultCacheDriver) {
$this->_useResultCache = true;
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver);
}
return $this;
}
@ -280,12 +263,13 @@ abstract class AbstractQuery
/**
* Returns the cache driver used for caching result sets.
*
* @deprecated
* @return Doctrine\Common\Cache\Cache Cache driver
*/
public function getResultCacheDriver()
{
if ($this->_resultCacheDriver) {
return $this->_resultCacheDriver;
if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $this->_queryCacheProfile->getResultCacheDriver();
} else {
return $this->_em->getConfiguration()->getResultCacheImpl();
}
@ -296,18 +280,17 @@ abstract class AbstractQuery
* how long and which ID to use for the cache entry.
*
* @param boolean $bool
* @param integer $timeToLive
* @param integer $lifetime
* @param string $resultCacheId
* @return Doctrine\ORM\AbstractQuery This query instance.
*/
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
{
$this->_useResultCache = $bool;
if ($timeToLive) {
$this->setResultCacheLifetime($timeToLive);
}
if ($resultCacheId) {
$this->_resultCacheId = $resultCacheId;
if ($bool) {
$this->setResultCacheLifetime($lifetime);
$this->setResultCacheId($resultCacheId);
} else {
$this->_queryCacheProfile = null;
}
return $this;
}
@ -315,27 +298,33 @@ abstract class AbstractQuery
/**
* Defines how long the result cache will be active before expire.
*
* @param integer $timeToLive How long the cache entry is valid.
* @param integer $lifetime How long the cache entry is valid.
* @return Doctrine\ORM\AbstractQuery This query instance.
*/
public function setResultCacheLifetime($timeToLive)
public function setResultCacheLifetime($lifetime)
{
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
if ($lifetime === null) {
$lifetime = 0;
} else {
$lifetime = (int)$lifetime;
}
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
} else {
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
}
$this->_resultCacheTTL = $timeToLive;
return $this;
}
/**
* Retrieves the lifetime of resultset cache.
*
* @deprecated
* @return integer
*/
public function getResultCacheLifetime()
{
return $this->_resultCacheTTL;
return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
}
/**
@ -360,6 +349,14 @@ abstract class AbstractQuery
return $this->_expireResultCache;
}
/**
* @return QueryCacheProfile
*/
public function getQueryCacheProfile()
{
return $this->_queryCacheProfile;
}
/**
* Change the default fetch mode of an association for this query.
*
@ -584,28 +581,6 @@ abstract class AbstractQuery
$this->setParameters($params);
}
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
list($key, $hash) = $this->getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
if ($cached === false || !isset($cached[$key])) {
// Cache miss.
$stmt = $this->_doExecute();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached[$key];
}
}
$stmt = $this->_doExecute();
if (is_numeric($stmt)) {
@ -627,43 +602,23 @@ abstract class AbstractQuery
*/
public function setResultCacheId($id)
{
$this->_resultCacheId = $id;
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
}
return $this;
}
/**
* 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.
* Get the result cache id to use to store the result set cache entry if set.
*
* @return array ($key, $hash)
* @deprecated
* @return string
*/
protected function getResultCacheId()
public function getResultCacheId()
{
if ($this->_resultCacheId) {
return array($this->_resultCacheId, $this->_resultCacheId);
} else {
$params = $this->_params;
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;
} else {
$params[$key] = $value;
}
}
$sql = $this->getSql();
ksort($this->_hints);
$key = implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
return array($key, md5($key));
}
return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
}
/**

View File

@ -209,27 +209,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['metadataDriverImpl'] : null;
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getResultCacheImpl()
{
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for the query cache (SQL cache).
*

View File

@ -57,17 +57,17 @@ final class NativeQuery extends AbstractQuery
*/
protected function _doExecute()
{
$stmt = $this->_em->getConnection()->prepare($this->_sql);
$params = $this->_params;
foreach ($params as $key => $value) {
if (isset($this->_paramTypes[$key])) {
$stmt->bindValue($key, $value, $this->_paramTypes[$key]);
} else {
$stmt->bindValue($key, $value);
$types = $this->_paramTypes;
if ($params) {
if (is_int(key($params))) {
ksort($params);
ksort($types);
$params = array_values($params);
$types = array_values($types);
}
}
$stmt->execute();
return $stmt;
return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile);
}
}

View File

@ -232,6 +232,9 @@ final class Query extends AbstractQuery
protected function _doExecute()
{
$executor = $this->_parse()->getSqlExecutor();
if ($this->_queryCacheProfile) {
$executor->setQueryCacheProfile($this->_queryCacheProfile);
}
// Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings();

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -22,6 +20,7 @@
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
/**
* Base class for SQL statement executors.
@ -34,8 +33,16 @@ use Doctrine\DBAL\Connection;
*/
abstract class AbstractSqlExecutor
{
/**
* @var array
*/
protected $_sqlStatements;
/**
* @var QueryCacheProfile
*/
protected $queryCacheProfile;
/**
* Gets the SQL statements that are executed by the executor.
*
@ -46,12 +53,18 @@ abstract class AbstractSqlExecutor
return $this->_sqlStatements;
}
public function setQueryCacheProfile(QueryCacheProfile $qcp)
{
$this->queryCacheProfile = $qcp;
}
/**
* Executes all sql statements.
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @param array $types The parameter types.
* @return Doctrine\DBAL\Driver\Statement
*/
abstract public function execute(Connection $conn, array $params, array $types);
abstract public function execute(Connection $conn, array $params, array $types);
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -32,7 +30,6 @@ use Doctrine\DBAL\Connection,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org
* @since 2.0
* @version $Revision$
*/
class MultiTableDeleteExecutor extends AbstractSqlExecutor
{
@ -102,11 +99,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
}
/**
* Executes all SQL statements.
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types)
{

View File

@ -141,11 +141,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
}
/**
* Executes all SQL statements.
*
* @param Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types)
{

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org
* @since 2.0
*/
@ -41,8 +38,11 @@ class SingleSelectExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
}
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types)
{
return $conn->executeQuery($this->_sqlStatements, $params, $types);
return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile);
}
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org
* @since 2.0
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
@ -45,7 +42,10 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST);
}
}
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types)
{
return $conn->executeUpdate($this->_sqlStatements, $params, $types);

@ -1 +1 @@
Subproject commit f91395b6f469b5076f52fefd64574c443b076485
Subproject commit dea79e7bfbabf973e807539e258ccf3b9ee98f45

View File

@ -90,10 +90,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCache()
{
$cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$query->useResultCache(true);
$query->setResultCacheDriver($cache);
$query->setResultCacheId('testing_result_cache_id');
$users = $query->getResult();
@ -108,11 +108,11 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCacheParams()
{
$cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$sqlCount = count($this->_sqlLoggerStack->queries);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1');
$query->setParameter(1, 1);
$query->setResultCacheDriver($cache);
$query->useResultCache(true);
$query->getResult();
@ -149,10 +149,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
}
/**
* @param <type> $query
* @param string $query
* @depends testNativeQueryResultCaching
*/
public function testResultCacheDependsOnQueryHints($query)
public function testResultCacheNotDependsOnQueryHints($query)
{
$cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache);
@ -160,7 +160,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setHint('foo', 'bar');
$query->getResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
$this->assertEquals($cacheCount, $this->getCacheSize($cache));
}
/**
@ -182,7 +182,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
* @param <type> $query
* @depends testNativeQueryResultCaching
*/
public function testResultCacheDependsOnHydrationMode($query)
public function testResultCacheNotDependsOnHydrationMode($query)
{
$cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache);
@ -190,7 +190,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
$query->getArrayResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
$this->assertEquals($cacheCount, $this->getCacheSize($cache));
}
/**