Skip to content

Commit

Permalink
doctrine#1277 DDC-3346 DDC-3531 - moved resultsetmapping into the new…
Browse files Browse the repository at this point in the history
…ly created `CachedPersisterContext`
  • Loading branch information
Ocramius committed Jan 22, 2015
1 parent c233d5c commit 8e2df99
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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;
}
Expand Down
55 changes: 29 additions & 26 deletions lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -216,6 +207,11 @@ class BasicEntityPersister implements EntityPersister
*/
private $identifierFlattener;

/**
* @var CachedPersisterContext[]
*/
protected $cachedPersisterContexts = [];

/**
* Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor.
Expand All @@ -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()
);
}

/**
Expand All @@ -246,7 +246,7 @@ public function getClassMetadata()
*/
public function getResultSetMapping()
{
return $this->rsm;
return $this->cachedPersisterContexts['noLimits']->rsm;
}

/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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));
}

/**
Expand Down Expand Up @@ -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));
}

/**
Expand Down Expand Up @@ -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));
}

/**
Expand All @@ -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']);
}

Expand All @@ -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']);
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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']) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down
121 changes: 121 additions & 0 deletions lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?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\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 <ocramius@gmail.com>
*/
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 8e2df99

Please sign in to comment.