4 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 * This software consists of voluntary contributions made by many individuals
17 * and is licensed under the MIT license. For more information, see
18 * <http://www.doctrine-project.org>.
21 namespace Doctrine\ORM\Cache\Persister\Entity
;
23 use Doctrine\ORM\Cache
;
24 use Doctrine\ORM\Cache\Region
;
25 use Doctrine\ORM\Cache\EntityCacheKey
;
26 use Doctrine\ORM\Cache\CollectionCacheKey
;
27 use Doctrine\ORM\Cache\TimestampCacheKey
;
28 use Doctrine\ORM\Cache\QueryCacheKey
;
29 use Doctrine\ORM\Cache\Persister\CachedPersister
;
30 use Doctrine\ORM\Cache\CacheException
;
31 use Doctrine\ORM\Mapping\ClassMetadata
;
32 use Doctrine\ORM\PersistentCollection
;
33 use Doctrine\ORM\EntityManagerInterface
;
34 use Doctrine\ORM\Persisters\Entity\EntityPersister
;
36 use Doctrine\Common\Util\ClassUtils
;
37 use Doctrine\Common\Collections\Criteria
;
40 * @author Fabio B. Silva <fabio.bat.silva@gmail.com>
43 abstract class AbstractEntityPersister
implements CachedEntityPersister
46 * @var \Doctrine\ORM\UnitOfWork
51 * @var \Doctrine\ORM\Mapping\ClassMetadataFactory
53 protected $metadataFactory;
56 * @var \Doctrine\ORM\Persisters\Entity\EntityPersister
61 * @var \Doctrine\ORM\Mapping\ClassMetadata
68 protected $queuedCache = array();
71 * @var \Doctrine\ORM\Cache\Region
76 * @var \Doctrine\ORM\Cache\TimestampRegion
78 protected $timestampRegion;
81 * @var \Doctrine\ORM\Cache\TimestampCacheKey
83 protected $timestampKey;
86 * @var \Doctrine\ORM\Cache\EntityHydrator
91 * @var \Doctrine\ORM\Cache
96 * @var \Doctrine\ORM\Cache\Logging\CacheLogger
98 protected $cacheLogger;
103 protected $regionName;
106 * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.
110 protected $joinedAssociations;
113 * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache.
114 * @param \Doctrine\ORM\Cache\Region $region The entity cache region.
115 * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
116 * @param \Doctrine\ORM\Mapping\ClassMetadata $class The entity metadata.
118 public function __construct(EntityPersister
$persister, Region
$region, EntityManagerInterface
$em, ClassMetadata
$class)
120 $configuration = $em->getConfiguration();
121 $cacheConfig = $configuration->getSecondLevelCacheConfiguration();
122 $cacheFactory = $cacheConfig->getCacheFactory();
124 $this->class = $class;
125 $this->region
= $region;
126 $this->persister
= $persister;
127 $this->cache
= $em->getCache();
128 $this->regionName
= $region->getName();
129 $this->uow
= $em->getUnitOfWork();
130 $this->metadataFactory
= $em->getMetadataFactory();
131 $this->cacheLogger
= $cacheConfig->getCacheLogger();
132 $this->timestampRegion
= $cacheFactory->getTimestampRegion();
133 $this->hydrator
= $cacheFactory->buildEntityHydrator($em, $class);
134 $this->timestampKey
= new TimestampCacheKey($this->class->rootEntityName
);
140 public function addInsert($entity)
142 $this->persister
->addInsert($entity);
148 public function getInserts()
150 return $this->persister
->getInserts();
156 public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null)
158 return $this->persister
->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy);
164 public function getCountSQL($criteria = array())
166 return $this->persister
->getCountSQL($criteria);
172 public function getInsertSQL()
174 return $this->persister
->getInsertSQL();
180 public function getResultSetMapping()
182 return $this->persister
->getResultSetMapping();
188 public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null)
190 return $this->persister
->getSelectConditionStatementSQL($field, $value, $assoc, $comparison);
196 public function exists($entity, Criteria
$extraConditions = null)
198 if (null === $extraConditions) {
199 $key = new EntityCacheKey($this->class->rootEntityName
, $this->class->getIdentifierValues($entity));
201 if ($this->region
->contains($key)) {
206 return $this->persister
->exists($entity, $extraConditions);
212 public function getCacheRegion()
214 return $this->region
;
218 * @return \Doctrine\ORM\Cache\EntityHydrator
220 public function getEntityHydrator()
222 return $this->hydrator
;
228 public function storeEntityCache($entity, EntityCacheKey
$key)
230 $class = $this->class;
231 $className = ClassUtils
::getClass($entity);
233 if ($className !== $this->class->name
) {
234 $class = $this->metadataFactory
->getMetadataFor($className);
237 if ($class->containsForeignIdentifier
) {
238 foreach ($class->associationMappings
as $name => $assoc) {
239 if (!empty($assoc['id']) && !isset($assoc['cache'])) {
240 throw CacheException
::nonCacheableEntityAssociation($class->name
, $name);
245 $entry = $this->hydrator
->buildCacheEntry($class, $key, $entity);
246 $cached = $this->region
->put($key, $entry);
248 if ($this->cacheLogger
&& $cached) {
249 $this->cacheLogger
->entityCachePut($this->regionName
, $key);
256 * @param object $entity
258 private function storeJoinedAssociations($entity)
260 if ($this->joinedAssociations
=== null) {
261 $associations = array();
263 foreach ($this->class->associationMappings
as $name => $assoc) {
264 if (isset($assoc['cache']) &&
265 ($assoc['type'] & ClassMetadata
::TO_ONE
) &&
266 ($assoc['fetch'] === ClassMetadata
::FETCH_EAGER ||
! $assoc['isOwningSide'])) {
268 $associations[] = $name;
272 $this->joinedAssociations
= $associations;
275 foreach ($this->joinedAssociations
as $name) {
276 $assoc = $this->class->associationMappings
[$name];
277 $assocEntity = $this->class->getFieldValue($entity, $name);
279 if ($assocEntity === null) {
283 $assocId = $this->uow
->getEntityIdentifier($assocEntity);
284 $assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId);
285 $assocPersister = $this->uow
->getEntityPersister($assoc['targetEntity']);
287 $assocPersister->storeEntityCache($assocEntity, $assocKey);
292 * Generates a string of currently query
294 * @param array $query
295 * @param string $criteria
296 * @param array $orderBy
297 * @param integer $limit
298 * @param integer $offset
302 protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
304 list($params) = ($criteria instanceof Criteria
)
305 ?
$this->persister
->expandCriteriaParameters($criteria)
306 : $this->persister
->expandParameters($criteria);
308 return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
314 public function expandParameters($criteria)
316 return $this->persister
->expandParameters($criteria);
322 public function expandCriteriaParameters(Criteria
$criteria)
324 return $this->persister
->expandCriteriaParameters($criteria);
330 public function getClassMetadata()
332 return $this->persister
->getClassMetadata();
338 public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
340 return $this->persister
->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit);
346 public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
348 return $this->persister
->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit);
354 public function getOwningTable($fieldName)
356 return $this->persister
->getOwningTable($fieldName);
362 public function executeInserts()
364 $this->queuedCache
['insert'] = $this->persister
->getInserts();
366 return $this->persister
->executeInserts();
372 public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = null, $limit = null, array $orderBy = null)
374 if ($entity !== null ||
$assoc !== null ||
! empty($hints) ||
$lockMode !== null) {
375 return $this->persister
->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy);
378 //handle only EntityRepository#findOneBy
379 $query = $this->persister
->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
380 $hash = $this->getHash($query, $criteria, null, null, null);
381 $rsm = $this->getResultSetMapping();
382 $queryKey = new QueryCacheKey($hash, 0, Cache
::MODE_NORMAL
, $this->timestampKey
);
383 $queryCache = $this->cache
->getQueryCache($this->regionName
);
384 $result = $queryCache->get($queryKey, $rsm);
386 if ($result !== null) {
387 if ($this->cacheLogger
) {
388 $this->cacheLogger
->queryCacheHit($this->regionName
, $queryKey);
394 if (($result = $this->persister
->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy)) === null) {
398 $cached = $queryCache->put($queryKey, $rsm, array($result));
400 if ($this->cacheLogger
) {
402 $this->cacheLogger
->queryCacheMiss($this->regionName
, $queryKey);
406 $this->cacheLogger
->queryCachePut($this->regionName
, $queryKey);
416 public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
418 $query = $this->persister
->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
419 $hash = $this->getHash($query, $criteria, null, null, null);
420 $rsm = $this->getResultSetMapping();
421 $queryKey = new QueryCacheKey($hash, 0, Cache
::MODE_NORMAL
, $this->timestampKey
);
422 $queryCache = $this->cache
->getQueryCache($this->regionName
);
423 $result = $queryCache->get($queryKey, $rsm);
425 if ($result !== null) {
426 if ($this->cacheLogger
) {
427 $this->cacheLogger
->queryCacheHit($this->regionName
, $queryKey);
433 $result = $this->persister
->loadAll($criteria, $orderBy, $limit, $offset);
434 $cached = $queryCache->put($queryKey, $rsm, $result);
436 if ($this->cacheLogger
) {
438 $this->cacheLogger
->queryCacheMiss($this->regionName
, $queryKey);
442 $this->cacheLogger
->queryCachePut($this->regionName
, $queryKey);
452 public function loadById(array $identifier, $entity = null)
454 $cacheKey = new EntityCacheKey($this->class->rootEntityName
, $identifier);
455 $cacheEntry = $this->region
->get($cacheKey);
456 $class = $this->class;
458 if ($cacheEntry !== null) {
459 if ($cacheEntry->class !== $this->class->name
) {
460 $class = $this->metadataFactory
->getMetadataFor($cacheEntry->class);
463 if (($entity = $this->hydrator
->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity)) !== null) {
464 if ($this->cacheLogger
) {
465 $this->cacheLogger
->entityCacheHit($this->regionName
, $cacheKey);
472 $entity = $this->persister
->loadById($identifier, $entity);
474 if ($entity === null) {
478 $class = $this->class;
479 $className = ClassUtils
::getClass($entity);
481 if ($className !== $this->class->name
) {
482 $class = $this->metadataFactory
->getMetadataFor($className);
485 $cacheEntry = $this->hydrator
->buildCacheEntry($class, $cacheKey, $entity);
486 $cached = $this->region
->put($cacheKey, $cacheEntry);
488 if ($cached && ($this->joinedAssociations
=== null ||
count($this->joinedAssociations
) > 0)) {
489 $this->storeJoinedAssociations($entity);
492 if ($this->cacheLogger
) {
494 $this->cacheLogger
->entityCachePut($this->regionName
, $cacheKey);
497 $this->cacheLogger
->entityCacheMiss($this->regionName
, $cacheKey);
506 public function count($criteria = array())
508 return $this->persister
->count($criteria);
514 public function loadCriteria(Criteria
$criteria)
516 $orderBy = $criteria->getOrderings();
517 $limit = $criteria->getMaxResults();
518 $offset = $criteria->getFirstResult();
519 $query = $this->persister
->getSelectSQL($criteria);
520 $hash = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
521 $rsm = $this->getResultSetMapping();
522 $queryKey = new QueryCacheKey($hash, 0, Cache
::MODE_NORMAL
, $this->timestampKey
);
523 $queryCache = $this->cache
->getQueryCache($this->regionName
);
524 $cacheResult = $queryCache->get($queryKey, $rsm);
526 if ($cacheResult !== null) {
527 if ($this->cacheLogger
) {
528 $this->cacheLogger
->queryCacheHit($this->regionName
, $queryKey);
534 $result = $this->persister
->loadCriteria($criteria);
535 $cached = $queryCache->put($queryKey, $rsm, $result);
537 if ($this->cacheLogger
) {
539 $this->cacheLogger
->queryCacheMiss($this->regionName
, $queryKey);
543 $this->cacheLogger
->queryCachePut($this->regionName
, $queryKey);
553 public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection
$coll)
555 $persister = $this->uow
->getCollectionPersister($assoc);
556 $hasCache = ($persister instanceof CachedPersister
);
560 $ownerId = $this->uow
->getEntityIdentifier($coll->getOwner());
561 $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId);
562 $list = $persister->loadCollectionCache($coll, $key);
564 if ($list !== null) {
565 if ($this->cacheLogger
) {
566 $this->cacheLogger
->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
573 $list = $this->persister
->loadManyToManyCollection($assoc, $sourceEntity, $coll);
576 $persister->storeCollectionCache($key, $list);
578 if ($this->cacheLogger
) {
579 $this->cacheLogger
->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
589 public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection
$coll)
591 $persister = $this->uow
->getCollectionPersister($assoc);
592 $hasCache = ($persister instanceof CachedPersister
);
595 $ownerId = $this->uow
->getEntityIdentifier($coll->getOwner());
596 $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId);
597 $list = $persister->loadCollectionCache($coll, $key);
599 if ($list !== null) {
600 if ($this->cacheLogger
) {
601 $this->cacheLogger
->collectionCacheHit($persister->getCacheRegion()->getName(), $key);
608 $list = $this->persister
->loadOneToManyCollection($assoc, $sourceEntity, $coll);
611 $persister->storeCollectionCache($key, $list);
613 if ($this->cacheLogger
) {
614 $this->cacheLogger
->collectionCacheMiss($persister->getCacheRegion()->getName(), $key);
624 public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array())
626 return $this->persister
->loadOneToOneEntity($assoc, $sourceEntity, $identifier);
632 public function lock(array $criteria, $lockMode)
634 $this->persister
->lock($criteria, $lockMode);
640 public function refresh(array $id, $entity, $lockMode = null)
642 $this->persister
->refresh($id, $entity, $lockMode);