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

Added multi-get support for second level cached collections

This commit is contained in:
Asmir Mustafic 2015-01-17 00:11:59 +01:00 committed by Marco Pivetta
parent 8f097ab304
commit 74e93e4cad
5 changed files with 253 additions and 4 deletions

View File

@ -21,6 +21,7 @@
namespace Doctrine\ORM\Cache;
use Doctrine\Common\Cache\CacheProvider;
use Doctrine\Common\Cache\MultiGetCache;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\Region;
@ -29,6 +30,8 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Cache\Region\DefaultRegion;
use Doctrine\ORM\Cache\Region\FileLockRegion;
use Doctrine\ORM\Cache\Region\UpdateTimestampCache;
use Doctrine\ORM\Cache\Region\DefaultMultiGetRegion;
use Doctrine\ORM\Cache\Persister\CachedPersister;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister;
@ -178,6 +181,19 @@ class DefaultCacheFactory implements CacheFactory
*/
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping)
{
if ($mapping['cache']) {
$targetPersister = $em->getUnitOfWork()->getEntityPersister($mapping['targetEntity']);
if ($targetPersister instanceof CachedPersister) {
$targetRegion = $targetPersister->getCacheRegion();
if ($targetRegion instanceof MultiGetRegion) {
return new MultiGetCollectionHydrator($em);
}
}
}
return new DefaultCollectionHydrator($em);
}
@ -202,7 +218,11 @@ class DefaultCacheFactory implements CacheFactory
$cacheAdapter->setNamespace($cache['region']);
$region = new DefaultRegion($cache['region'], $cacheAdapter, $this->regionsConfig->getLifetime($cache['region']));
if ($cacheAdapter instanceof MultiGetCache) {
$region = new DefaultMultiGetRegion($cache['region'], $cacheAdapter, $this->regionsConfig->getLifetime($cache['region']));
} else {
$region = new DefaultRegion($cache['region'], $cacheAdapter, $this->regionsConfig->getLifetime($cache['region']));
}
if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {

View File

@ -81,15 +81,34 @@ class DefaultCollectionHydrator implements CollectionHydrator
$targetRegion = $targetPersister->getCacheRegion();
$list = array();
$keys = array();
foreach ($entry->identifiers as $index => $identifier) {
$keys[$index] = new EntityCacheKey($assoc['targetEntity'], $identifier);
}
$entityEntry = $targetRegion->get(new EntityCacheKey($assoc['targetEntity'], $identifier));
if ($entityEntry === null) {
if ($targetRegion instanceof MultiGetRegion) {
$entityEntries = $targetRegion->getMulti($keys);
if ($entityEntries === null) {
return null;
}
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
foreach ($entityEntries as $index => $entityEntry) {
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->data, self::$hints);
}
} else {
foreach ($entry->identifiers as $index => $identifier) {
$entityEntry = $targetRegion->get(new EntityCacheKey($assoc['targetEntity'], $identifier));
if ($entityEntry === null) {
return null;
}
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
}
}
array_walk($list, function($entity, $index) use ($collection) {

View File

@ -0,0 +1,106 @@
<?php
/*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Query;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
/**
* Collection hydrator that expects a target region to be instance of {Doctrine\ORM\Cache\MultiGetRegion},
* to enable loading the entire collection with one cache request.
*
* @since 2.5
* @author Asmir Mustafic <goetas@gmail.com>
*/
class MultiGetCollectionHydrator implements CollectionHydrator
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $em;
/**
* @var \Doctrine\ORM\UnitOfWork
*/
private $uow;
/**
* @var array
*/
private static $hints = array(Query::HINT_CACHE_ENABLED => true);
/**
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->uow = $em->getUnitOfWork();
}
/**
* {@inheritdoc}
*/
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection)
{
$data = array();
foreach ($collection as $index => $entity) {
$data[$index] = $this->uow->getEntityIdentifier($entity);
}
return new CollectionCacheEntry($data);
}
/**
* {@inheritdoc}
*/
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection)
{
$assoc = $metadata->associationMappings[$key->association];
$targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
$targetRegion = $targetPersister->getCacheRegion();
$list = array();
$keys = array();
foreach ($entry->identifiers as $index => $identifier) {
$keys[$index] = new EntityCacheKey($assoc['targetEntity'], $identifier);
}
$entityEntries = $targetRegion->getMulti($keys);
if ($entityEntries === null) {
return null;
}
foreach ($entityEntries as $index => $entityEntry) {
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->data, self::$hints);
}
array_walk($list, function ($entity, $index) use ($collection) {
$collection->hydrateSet($index, $entity);
});
return $list;
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Cache;
use Doctrine\ORM\Cache\CacheKey;
/**
* Defines a region that supports multi-get reading.
*
* With one method call we can get multipe items.
*
* @since 2.5
* @author Asmir Mustafic
*/
interface MultiGetRegion extends Region
{
/**
* Get all items from the cache indentifed by $keys.
* It returns NULL if some elements can not be found.
*
* @param CacheKey[] $key The keys of the items to be retrieved.
* @return array The cached entries or NULL if one or more entries can not be found
*/
public function getMulti(array $keys);
}

View File

@ -0,0 +1,61 @@
<?php
/*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Cache\Region;
use Doctrine\ORM\Cache\Lock;
use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\Cache\CacheKey;
use Doctrine\ORM\Cache\CacheEntry;
use Doctrine\Common\Cache\CacheProvider;
use Doctrine\ORM\Cache\MultiGetRegion;
use Doctrine\Common\Cache\CacheMultiGet;
/**
* A cache region that enables the retrieval of multiple elements with one call
*
* @since 2.5
* @author Asmir Mustafic <goetas@gmail.com>
*/
class DefaultMultiGetRegion extends DefaultRegion implements MultiGetRegion
{
/**
* {@inheritdoc}
*/
public function getMulti(array $keys)
{
$keysToRetrieve = array();
foreach ($keys as $index => $key) {
$keysToRetrieve[$index] = $this->name . '_' . $key->hash;
}
$items = $this->cache->fetchMultiple($keysToRetrieve);
if (count($items) !== count($keysToRetrieve)) {
return null;
}
$returnableItems = array();
foreach ($keysToRetrieve as $index => $key) {
$returnableItems[$index] = $items[$key];
}
return $returnableItems;
}
}