diff --git a/lib/Doctrine/ORM/Cache/Persister/AbstractEntityPersister.php b/lib/Doctrine/ORM/Cache/Persister/AbstractEntityPersister.php index 3589e31a5..7ed41c1ec 100644 --- a/lib/Doctrine/ORM/Cache/Persister/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Cache/Persister/AbstractEntityPersister.php @@ -284,7 +284,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister */ protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null) { - list($params) = $this->persister->expandParameters($criteria); + list($params) = ($criteria instanceof Criteria) + ? $this->persister->expandCriteriaParameters($criteria) + : $this->persister->expandParameters($criteria); return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp); } @@ -297,6 +299,14 @@ abstract class AbstractEntityPersister implements CachedEntityPersister return $this->persister->expandParameters($criteria); } + /** + * {@inheritdoc} + */ + public function expandCriteriaParameters(Criteria $criteria) + { + return $this->persister->expandCriteriaParameters($criteria); + } + /** * {@inheritdoc} */ @@ -480,7 +490,36 @@ abstract class AbstractEntityPersister implements CachedEntityPersister */ public function loadCriteria(Criteria $criteria) { - return $this->persister->loadCriteria($criteria); + $query = $this->persister->getSelectSQL($criteria); + $timestamp = $this->timestampRegion->get($this->timestampKey); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $cacheResult = $queryCache->get($querykey, $rsm); + + if ($cacheResult !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $cacheResult; + } + + $result = $this->persister->loadCriteria($criteria); + $cached = $queryCache->put($querykey, $rsm, $result); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; } /** diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 6a20f6bd4..6504a3ee0 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -829,14 +829,9 @@ class BasicEntityPersister implements EntityPersister } /** - * Expands Criteria Parameters by walking the expressions and grabbing all - * parameters and types from it. - * - * @param \Doctrine\Common\Collections\Criteria $criteria - * - * @return array(array(), array()) + * {@inheritdoc} */ - private function expandCriteriaParameters(Criteria $criteria) + public function expandCriteriaParameters(Criteria $criteria) { $expression = $criteria->getWhereExpression(); diff --git a/lib/Doctrine/ORM/Persisters/EntityPersister.php b/lib/Doctrine/ORM/Persisters/EntityPersister.php index 6e7cc558c..d40c2c6af 100644 --- a/lib/Doctrine/ORM/Persisters/EntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/EntityPersister.php @@ -83,6 +83,15 @@ interface EntityPersister */ public function expandParameters($criteria); + /** + * Expands Criteria Parameters by walking the expressions and grabbing all parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function expandCriteriaParameters(Criteria $criteria); + /** * Gets the SQL WHERE condition for matching a field with a given value. * diff --git a/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractEntityPersisterTest.php b/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractEntityPersisterTest.php index ed4093a88..341db5ba6 100644 --- a/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractEntityPersisterTest.php +++ b/tests/Doctrine/Tests/ORM/Cache/Persister/AbstractEntityPersisterTest.php @@ -56,6 +56,7 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase 'getInsertSQL', 'getSelectSQL', 'expandParameters', + 'expandCriteriaParameters', 'getSelectConditionStatementSQL', 'addInsert', 'executeInserts', @@ -181,6 +182,19 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase $this->assertEquals(array('name'=>'Foo'), $persister->expandParameters(array('name'=>'Foo'))); } + public function testInvokeExpandCriteriaParameters() + { + $persister = $this->createPersisterDefault(); + $criteria = new Criteria(); + + $this->entityPersister->expects($this->once()) + ->method('expandCriteriaParameters') + ->with($this->equalTo($criteria)) + ->will($this->returnValue(array('name'=>'Foo'))); + + $this->assertEquals(array('name'=>'Foo'), $persister->expandCriteriaParameters($criteria)); + } + public function testInvokeSelectConditionStatementSQL() { $persister = $this->createPersisterDefault(); @@ -320,10 +334,18 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase public function testInvokeLoadCriteria() { + $rsm = new ResultSetMappingBuilder($this->em); $persister = $this->createPersisterDefault(); $entity = new Country("Foo"); $criteria = new Criteria(); + $this->em->getUnitOfWork()->registerManaged($entity, array('id'=>1), array('id'=>1, 'name'=>'Foo')); + $rsm->addEntityResult(Country::CLASSNAME, 'c'); + + $this->entityPersister->expects($this->once()) + ->method('getResultSetMapping') + ->will($this->returnValue($rsm)); + $this->entityPersister->expects($this->once()) ->method('loadCriteria') ->with($this->equalTo($criteria)) diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php new file mode 100644 index 000000000..fdb6c50eb --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheCriteriaTest.php @@ -0,0 +1,145 @@ +evictRegions(); + + $this->loadFixturesCountries(); + $this->evictRegions(); + $this->_em->clear(); + + $this->assertFalse($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId())); + + $repository = $this->_em->getRepository(Country::CLASSNAME); + $queryCount = $this->getCurrentQueryCount(); + $name = $this->countries[0]->getName(); + $result1 = $repository->matching(new Criteria( + Criteria::expr()->eq('name', $name) + )); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertEquals($this->countries[0]->getId(), $result1[0]->getId()); + $this->assertEquals($this->countries[0]->getName(), $result1[0]->getName()); + + $this->assertTrue($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId())); + + $this->_em->clear(); + + $result2 = $repository->matching(new Criteria( + Criteria::expr()->eq('name', $name) + )); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertCount(1, $result2); + + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\Country', $result2[0]); + + $this->assertEquals($result1[0]->getId(), $result2[0]->getId()); + $this->assertEquals($result1[0]->getName(), $result2[0]->getName()); + } + + public function testRepositoryMatching() + { + $this->evictRegions(); + + $this->loadFixturesCountries(); + $this->_em->clear(); + + $this->assertTrue($this->cache->containsEntity(Country::CLASSNAME, $this->countries[0]->getId())); + + $repository = $this->_em->getRepository(Country::CLASSNAME); + $queryCount = $this->getCurrentQueryCount(); + $result1 = $repository->matching(new Criteria( + Criteria::expr()->eq('name', $this->countries[0]->getName()) + )); + + $this->assertCount(1, $result1); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertEquals($this->countries[0]->getId(), $result1[0]->getId()); + $this->assertEquals($this->countries[0]->getName(), $result1[0]->getName()); + + $this->_em->clear(); + + $result2 = $repository->matching(new Criteria( + Criteria::expr()->eq('name', $this->countries[0]->getName()) + )); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertCount(1, $result2); + + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\Country', $result2[0]); + + $this->assertEquals($this->countries[0]->getId(), $result2[0]->getId()); + $this->assertEquals($this->countries[0]->getName(), $result2[0]->getName()); + + $result3 = $repository->matching(new Criteria( + Criteria::expr()->eq('name', $this->countries[1]->getName()) + )); + + $this->assertEquals($queryCount + 2, $this->getCurrentQueryCount()); + $this->assertCount(1, $result3); + + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\Country', $result3[0]); + + $this->assertEquals($this->countries[1]->getId(), $result3[0]->getId()); + $this->assertEquals($this->countries[1]->getName(), $result3[0]->getName()); + + $result4 = $repository->matching(new Criteria( + Criteria::expr()->eq('name', $this->countries[1]->getName()) + )); + + $this->assertEquals($queryCount + 2, $this->getCurrentQueryCount()); + $this->assertCount(1, $result4); + + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\Country', $result4[0]); + + $this->assertEquals($this->countries[1]->getId(), $result4[0]->getId()); + $this->assertEquals($this->countries[1]->getName(), $result4[0]->getName()); + } + + public function testCollectionMatching() + { + $this->loadFixturesCountries(); + $this->loadFixturesStates(); + $this->loadFixturesCities(); + $this->_em->clear(); + $this->secondLevelCacheLogger->clearStats(); + + $entity = $this->_em->find(State::CLASSNAME, $this->states[0]->getId()); + $itemName = $this->states[0]->getCities()->get(0)->getName(); + $queryCount = $this->getCurrentQueryCount(); + $collection = $entity->getCities(); + $matching = $collection->matching(new Criteria( + Criteria::expr()->eq('name', $itemName) + )); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $matching); + $this->assertCount(1, $matching); + + $this->_em->clear(); + + $entity = $this->_em->find(State::CLASSNAME, $this->states[0]->getId()); + $queryCount = $this->getCurrentQueryCount(); + $collection = $entity->getCities(); + $matching = $collection->matching(new Criteria( + Criteria::expr()->eq('name', $itemName) + )); + + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $matching); + $this->assertCount(1, $matching); + } + +}