diff --git a/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php b/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php
index edb39afc634..e8f9801cc9d 100644
--- a/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php
+++ b/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php
@@ -66,7 +66,7 @@ protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r'
$sql = $this->getSQLTableAlias($class->name, $tableAlias) . '.'
. $this->quoteStrategy->getColumnName($field, $class, $this->platform);
- $this->rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
+ $this->cachedPersisterContexts['noLimits']->rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
if (isset($class->fieldMappings[$field]['requireSQLConversion'])) {
$type = Type::getType($class->getTypeOfField($field));
@@ -88,7 +88,7 @@ protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $classNa
{
$columnAlias = $this->getSQLColumnAlias($joinColumnName);
- $this->rsm->addMetaResult('r', $columnAlias, $joinColumnName, false, $type);
+ $this->cachedPersisterContexts['noLimits']->rsm->addMetaResult('r', $columnAlias, $joinColumnName, false, $type);
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
}
diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
index c53e817c151..30382581181 100644
--- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
+++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
@@ -134,15 +134,6 @@ class BasicEntityPersister implements EntityPersister
*/
protected $queuedInserts = array();
- /**
- * ResultSetMapping that is used for all queries. Is generated lazily once per request.
- *
- * TODO: Evaluate Caching in combination with the other cached SQL snippets.
- *
- * @var Query\ResultSetMapping
- */
- protected $rsm;
-
/**
* The map of column names to DBAL mapping types of all prepared columns used
* when INSERTing or UPDATEing an entity.
@@ -216,6 +207,11 @@ class BasicEntityPersister implements EntityPersister
*/
private $identifierFlattener;
+ /**
+ * @var CachedPersisterContext[]
+ */
+ protected $cachedPersisterContexts = [];
+
/**
* Initializes a new BasicEntityPersister that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor.
@@ -231,6 +227,10 @@ public function __construct(EntityManagerInterface $em, ClassMetadata $class)
$this->platform = $this->conn->getDatabasePlatform();
$this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
$this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory());
+ $this->cachedPersisterContexts['noLimits'] = new CachedPersisterContext(
+ $class,
+ new Query\ResultSetMapping()
+ );
}
/**
@@ -246,7 +246,7 @@ public function getClassMetadata()
*/
public function getResultSetMapping()
{
- return $this->rsm;
+ return $this->cachedPersisterContexts['noLimits']->rsm;
}
/**
@@ -720,7 +720,7 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint
}
$hydrator = $this->em->newHydrator($this->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
- $entities = $hydrator->hydrateAll($stmt, $this->rsm, $hints);
+ $entities = $hydrator->hydrateAll($stmt, $this->cachedPersisterContexts['noLimits']->rsm, $hints);
return $entities ? $entities[0] : null;
}
@@ -809,7 +809,7 @@ public function refresh(array $id, $entity, $lockMode = null)
$stmt = $this->conn->executeQuery($sql, $params, $types);
$hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT);
- $hydrator->hydrateAll($stmt, $this->rsm, array(Query::HINT_REFRESH => true));
+ $hydrator->hydrateAll($stmt, $this->cachedPersisterContexts['noLimits']->rsm, array(Query::HINT_REFRESH => true));
}
/**
@@ -841,7 +841,7 @@ public function loadCriteria(Criteria $criteria)
$stmt = $this->conn->executeQuery($query, $params, $types);
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
- return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
+ return $hydrator->hydrateAll($stmt, $this->cachedPersisterContexts['noLimits']->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
}
/**
@@ -886,7 +886,7 @@ public function loadAll(array $criteria = array(), array $orderBy = null, $limit
$hydrator = $this->em->newHydrator(($this->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
- return $hydrator->hydrateAll($stmt, $this->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
+ return $hydrator->hydrateAll($stmt, $this->cachedPersisterContexts['noLimits']->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true));
}
/**
@@ -909,11 +909,11 @@ public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = n
*/
private function loadArrayFromStatement($assoc, $stmt)
{
- $rsm = $this->rsm;
+ $rsm = $this->cachedPersisterContexts['noLimits']->rsm;
$hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true);
if (isset($assoc['indexBy'])) {
- $rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed.
+ $rsm = clone ($this->cachedPersisterContexts['noLimits']->rsm); // this is necessary because the "default rsm" should be changed.
$rsm->addIndexBy('r', $assoc['indexBy']);
}
@@ -931,14 +931,14 @@ private function loadArrayFromStatement($assoc, $stmt)
*/
private function loadCollectionFromStatement($assoc, $stmt, $coll)
{
- $rsm = $this->rsm;
+ $rsm = $this->cachedPersisterContexts['noLimits']->rsm;
$hints = array(
UnitOfWork::HINT_DEFEREAGERLOAD => true,
'collection' => $coll
);
if (isset($assoc['indexBy'])) {
- $rsm = clone ($this->rsm); // this is necessary because the "default rsm" should be changed.
+ $rsm = clone ($this->cachedPersisterContexts['noLimits']->rsm); // this is necessary because the "default rsm" should be changed.
$rsm->addIndexBy('r', $assoc['indexBy']);
}
@@ -1062,7 +1062,7 @@ public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit
break;
}
- $columnList = $this->getSelectColumnsSQL();
+ $columnList = $this->getSelectColumnsSQL(null !== $limit);
$tableAlias = $this->getSQLTableAlias($this->class->name);
$filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias);
$tableName = $this->quoteStrategy->getTableName($this->class, $this->platform);
@@ -1180,17 +1180,19 @@ protected final function getOrderBySQL(array $orderBy, $baseTableAlias)
* the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}.
* Subclasses may or may not do the same.
*
+ * @param bool $hasLimitClause
+ *
* @return string The SQL fragment.
*/
- protected function getSelectColumnsSQL()
+ protected function getSelectColumnsSQL(/*$hasLimitClause = false*/)
{
+ //if ( ! $hasLimitClause && $this->selectColumnListSql !== null) {
if ($this->selectColumnListSql !== null) {
return $this->selectColumnListSql;
}
$columnList = array();
- $this->rsm = new Query\ResultSetMapping();
- $this->rsm->addEntityResult($this->class->name, 'r'); // r for root
+ $this->cachedPersisterContexts['noLimits']->rsm->addEntityResult($this->class->name, 'r'); // r for root
// Add regular columns to select list
foreach ($this->class->fieldNames as $field) {
@@ -1210,6 +1212,7 @@ protected function getSelectColumnsSQL()
$isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide'];
$isAssocFromOneEager = $assoc['type'] !== ClassMetadata::MANY_TO_MANY && $assoc['fetch'] === ClassMetadata::FETCH_EAGER;
+ //if ($hasLimitClause || ! ($isAssocFromOneEager || $isAssocToOneInverseSide)) {
if ( ! ($isAssocFromOneEager || $isAssocToOneInverseSide)) {
continue;
}
@@ -1221,7 +1224,7 @@ protected function getSelectColumnsSQL()
}
$assocAlias = 'e' . ($eagerAliasCounter++);
- $this->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
+ $this->cachedPersisterContexts['noLimits']->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
foreach ($eagerEntity->fieldNames as $field) {
$columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias);
@@ -1241,7 +1244,7 @@ protected function getSelectColumnsSQL()
$joinCondition = array();
if (isset($assoc['indexBy'])) {
- $this->rsm->addIndexBy($assocAlias, $assoc['indexBy']);
+ $this->cachedPersisterContexts['noLimits']->rsm->addIndexBy($assocAlias, $assoc['indexBy']);
}
if ( ! $assoc['isOwningSide']) {
@@ -1318,7 +1321,7 @@ protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $
. '.' . $quotedColumn . ' AS ' . $resultColumnName;
$type = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
- $this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
+ $this->cachedPersisterContexts['noLimits']->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
}
return implode(', ', $columnList);
@@ -1456,7 +1459,7 @@ protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r'
$sql = $tableAlias . '.' . $columnName;
$columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]);
- $this->rsm->addFieldResult($alias, $columnAlias, $field);
+ $this->cachedPersisterContexts['noLimits']->rsm->addFieldResult($alias, $columnAlias, $field);
if (isset($class->fieldMappings[$field]['requireSQLConversion'])) {
$type = Type::getType($class->getTypeOfField($field));
diff --git a/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php b/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php
new file mode 100644
index 00000000000..a148a54dda7
--- /dev/null
+++ b/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php
@@ -0,0 +1,121 @@
+.
+ */
+
+namespace Doctrine\ORM\Persisters\Entity;
+use Doctrine\Common\Persistence\Mapping\ClassMetadata;
+use Doctrine\ORM\Query\ResultSetMapping;
+
+/**
+ * A swappable persister context to use as a container for the current
+ * generated query/resultSetMapping/type binding information.
+ *
+ * This class is a utility class to be used only by the persister API
+ *
+ * This object is highly mutable due to performance reasons. Same reasoning
+ * behind its properties being public.
+ *
+ * @author Marco Pivetta
+ */
+class CachedPersisterContext
+{
+ /**
+ * Metadata object that describes the mapping of the mapped entity class.
+ *
+ * @var \Doctrine\ORM\Mapping\ClassMetadata
+ */
+ public $class;
+
+ /**
+ * ResultSetMapping that is used for all queries. Is generated lazily once per request.
+ *
+ * @var \Doctrine\ORM\Query\ResultSetMapping
+ */
+ public $rsm;
+
+ /**
+ * The map of column names to DBAL mapping types of all prepared columns used
+ * when INSERTing or UPDATEing an entity.
+ *
+ * @var array
+ *
+ * @see \Doctrine\ORM\Persisters\Entity\BasicEntityPersister#prepareInsertData($entity)
+ * @see \Doctrine\ORM\Persisters\Entity\BasicEntityPersister#prepareUpdateData($entity)
+ */
+ public $columnTypes = array();
+
+ /**
+ * The map of quoted column names.
+ *
+ * @var array
+ *
+ * @see \Doctrine\ORM\Persisters\Entity\BasicEntityPersister#prepareInsertData($entity)
+ * @see \Doctrine\ORM\Persisters\Entity\BasicEntityPersister#prepareUpdateData($entity)
+ */
+ public $quotedColumns = array();
+
+ /**
+ * The INSERT SQL statement used for entities handled by this persister.
+ * This SQL is only generated once per request, if at all.
+ *
+ * @var string
+ */
+ public $insertSql = '';
+
+ /**
+ * The SELECT column list SQL fragment used for querying entities by this persister.
+ * This SQL fragment is only generated once per request, if at all.
+ *
+ * @var string
+ */
+ public $selectColumnListSql;
+
+ /**
+ * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one
+ * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations.
+ *
+ * @var string
+ */
+ public $selectJoinSql;
+
+ /**
+ * Counter for creating unique SQL table and column aliases.
+ *
+ * @var integer
+ */
+ public $sqlAliasCounter = 0;
+
+ /**
+ * Map from class names (FQCN) to the corresponding generated SQL table aliases.
+ *
+ * @var array
+ */
+ public $sqlTableAliases = array();
+
+ /**
+ * @param ClassMetadata $class
+ * @param ResultSetMapping $rsm
+ */
+ public function __construct(
+ ClassMetadata $class,
+ ResultSetMapping $rsm
+ ) {
+ $this->class = $class;
+ $this->rsm = $rsm;
+ }
+}
diff --git a/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php
index 019d987d5fb..3ec9afc309f 100644
--- a/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php
+++ b/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php
@@ -425,14 +425,14 @@ protected function getSelectColumnsSQL()
}
$columnList = array();
- $this->rsm = new ResultSetMapping();
+ //$this->cachedPersisterContexts['noLimits']->rsm = new ResultSetMapping();
$discrColumn = $this->class->discriminatorColumn['name'];
$baseTableAlias = $this->getSQLTableAlias($this->class->name);
$resultColumnName = $this->platform->getSQLResultCasing($discrColumn);
- $this->rsm->addEntityResult($this->class->name, 'r');
- $this->rsm->setDiscriminatorColumn('r', $resultColumnName);
- $this->rsm->addMetaResult('r', $resultColumnName, $discrColumn);
+ $this->cachedPersisterContexts['noLimits']->rsm->addEntityResult($this->class->name, 'r');
+ $this->cachedPersisterContexts['noLimits']->rsm->setDiscriminatorColumn('r', $resultColumnName);
+ $this->cachedPersisterContexts['noLimits']->rsm->addMetaResult('r', $resultColumnName, $discrColumn);
// Add regular columns
foreach ($this->class->fieldMappings as $fieldName => $mapping) {
diff --git a/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php
index 23ebdf2d2d9..b93c2afc9e5 100644
--- a/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php
+++ b/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php
@@ -63,8 +63,8 @@ protected function getSelectColumnsSQL()
$resultColumnName = $this->platform->getSQLResultCasing($discrColumn);
- $this->rsm->setDiscriminatorColumn('r', $resultColumnName);
- $this->rsm->addMetaResult('r', $resultColumnName, $discrColumn);
+ $this->cachedPersisterContexts['noLimits']->rsm->setDiscriminatorColumn('r', $resultColumnName);
+ $this->cachedPersisterContexts['noLimits']->rsm->addMetaResult('r', $resultColumnName, $discrColumn);
// Append subclass columns
foreach ($this->class->subClasses as $subClassName) {