1
0
mirror of synced 2024-12-05 03:06:05 +03:00

handle update/delete queries

This commit is contained in:
fabios 2013-10-15 17:55:10 -04:00
parent 61bff7d5f6
commit d135e402bb
4 changed files with 123 additions and 16 deletions

View File

@ -829,13 +829,13 @@ However, you can use the cache API to check / invalidate cache entries.
/* var $cache \Doctrine\ORM\Cache */
$cache = $em->getCache();
$cache->containsEntity('State', 1) // Check if the cache exists
$cache->evictEntity('State', 1); // Remove an entity from cache
$cache->evictEntityRegion('State'); // Remove all entities from cache
$cache->containsEntity('Entity\State', 1) // Check if the cache exists
$cache->evictEntity('Entity\State', 1); // Remove an entity from cache
$cache->evictEntityRegion('Entity\State'); // Remove all entities from cache
$cache->containsCollection('State', 'cities', 1); // Check if the cache exists
$cache->evictCollection('State', 'cities', 1); // Remove an entity collection from cache
$cache->evictCollectionRegion('State', 'cities'); // Remove all collections from cache
$cache->containsCollection('Entity\State', 'cities', 1); // Check if the cache exists
$cache->evictCollection('Entity\State', 'cities', 1); // Remove an entity collection from cache
$cache->evictCollectionRegion('Entity\State', 'cities'); // Remove all collections from cache
Limitations
-----------
@ -843,10 +843,8 @@ Limitations
Composite primary key
~~~~~~~~~~~~~~~~~~~~~
.. note::
Composite primary key are supported by second level cache, however when one of the keys is an association
the cached entity should always be retrieved using the association identifier.
Composite primary key are supported by second level cache, however when one of the keys is an association
the cached entity should always be retrieved using the association identifier.
.. code-block:: php
@ -895,4 +893,47 @@ A ``Doctrine\\ORM\\Cache\\ConcurrentRegion`` is designed to store concurrently m
By default, Doctrine provides a very simple implementation based on file locks ``Doctrine\\ORM\\Cache\\Region\\FileLockRegion``.
If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region.
for more details about how to implement a cache region please see :ref:`reference-second-level-cache-regions`
for more details about how to implement a cache region please see :ref:`reference-second-level-cache-regions`
DELETE / UPDATE queries
~~~~~~~~~~~~~~~~~~~~~~~
DQL UPDATE / DELETE statements are ported directly into a database and bypass the second-level cache,
Entities that are already cached will NOT be invalidated.
However the cached data could be evicted using the cache API or an special query hint.
Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT``
.. code-block:: php
<?php
// Execute and invalidate
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->setHint(Query::HINT_CACHE_EVICT, true)
->execute();
Execute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API
.. code-block:: php
<?php
// Execute
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->execute();
// Invoke Cache API
$em->getCache()->evictEntityRegion('Entity\Country');
Execute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API
.. code-block:: php
<?php
// Execute
$this->_em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1")
->execute();
// Invoke Cache API
$em->getCache()->evictEntity('Entity\Country', 1);

View File

@ -129,6 +129,11 @@ abstract class AbstractQuery
*/
protected $cacheable = false;
/**
* @var boolean
*/
protected $hasCache = false;
/**
* Second level cache region name.
*
@ -162,9 +167,10 @@ abstract class AbstractQuery
{
$this->_em = $em;
$this->parameters = new ArrayCollection();
$this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
if ($this->_em->getConfiguration()->isSecondLevelCacheEnabled()) {
$this->cacheLogger = $em->getConfiguration()
if ($this->hasCache) {
$this->cacheLogger = $em->getConfiguration()
->getSecondLevelCacheConfiguration()
->getCacheLogger();
}
@ -220,7 +226,7 @@ abstract class AbstractQuery
*/
protected function isCacheEnabled()
{
return $this->cacheable && $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
return $this->cacheable && $this->hasCache;
}
/**

View File

@ -64,6 +64,11 @@ final class Query extends AbstractQuery
*/
const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
/**
* @var string
*/
const HINT_CACHE_EVICT = 'doctrine.cache.evict';
/**
* Internal hint: is set to the proxy entity that is currently triggered for loading
*
@ -287,11 +292,34 @@ final class Query extends AbstractQuery
throw QueryException::invalidParameterNumber();
}
// evict all cache for the entity region
if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
$this->evictEntityCacheRegion();
}
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
}
/**
* Evict entity cache region
*/
private function evictEntityCacheRegion()
{
$AST = $this->getAST();
if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
}
$className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
? $AST->deleteClause->abstractSchemaName
: $AST->updateClause->abstractSchemaName;
$this->_em->getCache()->evictEntityRegion($className);
}
/**
* Processes query parameter mappings.
*

View File

@ -827,6 +827,38 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount('bar_region'));
}
public function testHintClearEntityRegionUpdateStatement()
{
$this->evictRegions();
$this->loadFixturesCountries();
$this->assertTrue($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[0]->getId()));
$this->assertTrue($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[1]->getId()));
$this->_em->createQuery('DELETE Doctrine\Tests\Models\Cache\Country u WHERE u.id = 4')
->setHint(Query::HINT_CACHE_EVICT, true)
->execute();
$this->assertFalse($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[0]->getId()));
$this->assertFalse($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[1]->getId()));
}
public function testHintClearEntityRegionDeleteStatement()
{
$this->evictRegions();
$this->loadFixturesCountries();
$this->assertTrue($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[0]->getId()));
$this->assertTrue($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[1]->getId()));
$this->_em->createQuery("UPDATE Doctrine\Tests\Models\Cache\Country u SET u.name = 'foo' WHERE u.id = 1")
->setHint(Query::HINT_CACHE_EVICT, true)
->execute();
$this->assertFalse($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[0]->getId()));
$this->assertFalse($this->cache->containsEntity('Doctrine\Tests\Models\Cache\Country', $this->countries[1]->getId()));
}
/**
* @expectedException \Doctrine\ORM\Cache\CacheException
* @expectedExceptionMessage Second level cache does not support partial entities.
@ -848,7 +880,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
*/
public function testNonCacheableQueryDeleteStatementException()
{
$this->_em->createQuery('DELETE Doctrine\Tests\Models\Cache\Country u WHERE u.id = 4')
$this->_em->createQuery("DELETE Doctrine\Tests\Models\Cache\Country u WHERE u.id = 4")
->setCacheable(true)
->getResult();
}
@ -859,7 +891,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest
*/
public function testNonCacheableQueryUpdateStatementException()
{
$this->_em->createQuery('UPDATE Doctrine\Tests\Models\Cache\Country u SET u.name = NULL WHERE u.id = 4')
$this->_em->createQuery("UPDATE Doctrine\Tests\Models\Cache\Country u SET u.name = 'foo' WHERE u.id = 4")
->setCacheable(true)
->getResult();
}