Commit e2d678cc authored by romanb's avatar romanb

[2.0] Refactorings to reduce duplicated code and increase efficiency.

parent c54d5825
...@@ -42,30 +42,22 @@ class ObjectHydrator extends AbstractHydrator ...@@ -42,30 +42,22 @@ class ObjectHydrator extends AbstractHydrator
/* The following parts are reinitialized on every hydration run. */ /* The following parts are reinitialized on every hydration run. */
private $_allowPartialObjects = false;
private $_identifierMap; private $_identifierMap;
private $_resultPointers; private $_resultPointers;
private $_idTemplate; private $_idTemplate;
private $_resultCounter; private $_resultCounter;
private $_fetchedAssociations;
private $_rootAliases = array(); private $_rootAliases = array();
private $_initializedCollections = array(); private $_initializedCollections = array();
private $_existingCollections = array(); private $_existingCollections = array();
private $_proxyFactory;
//private $_createdEntities; //private $_createdEntities;
/** @override */ /** @override */
protected function _prepare() protected function _prepare()
{ {
$this->_allowPartialObjects = isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]);
$this->_proxyFactory = $this->_em->getProxyFactory();
$this->_identifierMap = $this->_identifierMap =
$this->_resultPointers = $this->_resultPointers =
$this->_idTemplate = $this->_idTemplate = array();
$this->_fetchedAssociations = array();
$this->_resultCounter = 0; $this->_resultCounter = 0;
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
...@@ -85,13 +77,13 @@ class ObjectHydrator extends AbstractHydrator ...@@ -85,13 +77,13 @@ class ObjectHydrator extends AbstractHydrator
$targetClass = $this->_getClassMetadata($targetClassName); $targetClass = $this->_getClassMetadata($targetClassName);
$this->_ce[$targetClassName] = $targetClass; $this->_ce[$targetClassName] = $targetClass;
$assoc = $targetClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; $assoc = $targetClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_fetchedAssociations[$assoc->sourceEntityName][$assoc->sourceFieldName] = true; $this->_hints['fetched'][$assoc->sourceEntityName][$assoc->sourceFieldName] = true;
if ($assoc->mappedByFieldName) { if ($assoc->mappedByFieldName) {
$this->_fetchedAssociations[$assoc->targetEntityName][$assoc->mappedByFieldName] = true; $this->_hints['fetched'][$assoc->targetEntityName][$assoc->mappedByFieldName] = true;
} else { } else {
if (isset($targetClass->inverseMappings[$className][$assoc->sourceFieldName])) { if (isset($targetClass->inverseMappings[$className][$assoc->sourceFieldName])) {
$inverseAssoc = $targetClass->inverseMappings[$className][$assoc->sourceFieldName]; $inverseAssoc = $targetClass->inverseMappings[$className][$assoc->sourceFieldName];
$this->_fetchedAssociations[$assoc->targetEntityName][$inverseAssoc->sourceFieldName] = true; $this->_hints['fetched'][$assoc->targetEntityName][$inverseAssoc->sourceFieldName] = true;
} }
} }
} }
...@@ -186,53 +178,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -186,53 +178,7 @@ class ObjectHydrator extends AbstractHydrator
unset($data[$discrColumn]); unset($data[$discrColumn]);
} }
$entity = $this->_uow->createEntity($className, $data, $this->_hints); return $this->_uow->createEntity($className, $data, $this->_hints);
//FIXME: If $entity comes from the identity map there is no need to do this!
// Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! $this->_allowPartialObjects) {
$oid = spl_object_hash($entity);
foreach ($this->_getClassMetadata($className)->associationMappings as $field => $assoc) {
// Check if the association is not among the fetch-joined associatons already.
if ( ! isset($this->_fetchedAssociations[$className][$field])) {
if ($assoc->isOneToOne()) {
$joinColumns = array();
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$joinColumns[$srcColumn] = $data[$assoc->joinColumnFieldNames[$srcColumn]];
}
//TODO: If its in the identity map just get it from there if possible!
if ($assoc->isLazilyFetched() /*&& ! $assoc->isOptional*/) {
// Inject proxy
$proxy = $this->_proxyFactory->getAssociationProxy($entity, $assoc, $joinColumns);
$this->_uow->setOriginalEntityProperty($oid, $field, $proxy);
$this->_ce[$className]->reflFields[$field]->setValue($entity, $proxy);
} else {
// Eager load
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumns);
}
} else {
// Inject collection
$reflField = $this->_ce[$className]->reflFields[$field];
$pColl = new PersistentCollection($this->_em,
$this->_getClassMetadata($assoc->targetEntityName),
$reflField->getValue($entity) ?: new ArrayCollection
);
$pColl->setOwner($entity, $assoc);
$reflField->setValue($entity, $pColl);
if ($assoc->isLazilyFetched()) {
$pColl->setInitialized(false);
} else {
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, $pColl, $this->_em);
}
$this->_uow->setOriginalEntityProperty($oid, $field, $pColl);
}
}
}
}
return $entity;
} }
private function _getEntityFromIdentityMap($className, array $data) private function _getEntityFromIdentityMap($className, array $data)
......
...@@ -186,6 +186,7 @@ class ClassMetadataFactory ...@@ -186,6 +186,7 @@ class ClassMetadataFactory
$class->setVersioned($parent->isVersioned); $class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField); $class->setVersionField($parent->versionField);
$class->setDiscriminatorMap($parent->discriminatorMap); $class->setDiscriminatorMap($parent->discriminatorMap);
$class->resultColumnNames = $parent->resultColumnNames;
} }
// Invoke driver // Invoke driver
......
...@@ -282,6 +282,7 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -282,6 +282,7 @@ class JoinedSubclassPersister extends StandardEntityPersister
$tableAliases[$className] = 't' . $aliasIndex++; $tableAliases[$className] = 't' . $aliasIndex++;
} }
// Add regular columns
$columnList = ''; $columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) { foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
$tableAlias = isset($mapping['inherited']) ? $tableAlias = isset($mapping['inherited']) ?
...@@ -290,6 +291,15 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -290,6 +291,15 @@ class JoinedSubclassPersister extends StandardEntityPersister
$columnList .= $tableAlias . '.' . $this->_class->getQuotedColumnName($fieldName, $this->_platform); $columnList .= $tableAlias . '.' . $this->_class->getQuotedColumnName($fieldName, $this->_platform);
} }
// Add foreign key columns
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$columnList .= ', ' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform);
}
}
}
// Add discriminator column // Add discriminator column
if ($this->_class->rootEntityName == $this->_class->name) { if ($this->_class->rootEntityName == $this->_class->name) {
$columnList .= ', ' . $baseTableAlias . '.' . $columnList .= ', ' . $baseTableAlias . '.' .
...@@ -315,6 +325,7 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -315,6 +325,7 @@ class JoinedSubclassPersister extends StandardEntityPersister
// OUTER JOIN sub tables // OUTER JOIN sub tables
foreach ($this->_class->subClasses as $subClassName) { foreach ($this->_class->subClasses as $subClassName) {
//FIXME: Add columns and foreign key columns of inherited classes to the select list
$subClass = $this->_em->getClassMetadata($subClassName); $subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $tableAliases[$subClassName]; $tableAlias = $tableAliases[$subClassName];
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
......
...@@ -27,6 +27,7 @@ use Doctrine\Common\DoctrineException, ...@@ -27,6 +27,7 @@ use Doctrine\Common\DoctrineException,
Doctrine\DBAL\Types\Type, Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager, Doctrine\ORM\EntityManager,
Doctrine\ORM\UnitOfWork, Doctrine\ORM\UnitOfWork,
Doctrine\ORM\Query,
Doctrine\ORM\PersistentCollection, Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Events; Doctrine\ORM\Events;
...@@ -236,9 +237,8 @@ class StandardEntityPersister ...@@ -236,9 +237,8 @@ class StandardEntityPersister
if ($isVersioned = $this->_class->isVersioned) { if ($isVersioned = $this->_class->isVersioned) {
$versionField = $this->_class->versionField; $versionField = $this->_class->versionField;
$versionFieldType = $this->_class->getTypeOfField($versionField); $versionFieldType = $this->_class->getTypeOfField($versionField);
$where[$this->_class->fieldNames[$versionField]] = Type::getType( $where[$versionField] = Type::getType($versionFieldType)
$this->_class->fieldMappings[$versionField]['type'] ->convertToDatabaseValue($entity->version, $this->_platform);
)->convertToDatabaseValue($entity->version, $this->_platform);
$versionFieldColumnName = $this->_class->getQuotedColumnName($versionField, $this->_platform); $versionFieldColumnName = $this->_class->getQuotedColumnName($versionField, $this->_platform);
if ($versionFieldType == 'integer') { if ($versionFieldType == 'integer') {
$set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1'; $set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1';
...@@ -504,16 +504,15 @@ class StandardEntityPersister ...@@ -504,16 +504,15 @@ class StandardEntityPersister
} else if ($this->_class->discriminatorColumn !== null && $column == $this->_class->discriminatorColumn['name']) { } else if ($this->_class->discriminatorColumn !== null && $column == $this->_class->discriminatorColumn['name']) {
$entityName = $this->_class->discriminatorMap[$value]; $entityName = $this->_class->discriminatorMap[$value];
} else { } else {
$data[$column] = $value;
$joinColumnValues[$column] = $value; $joinColumnValues[$column] = $value;
} }
} }
if ($entity === null) { $hints = array();
$entity = $this->_em->getUnitOfWork()->createEntity($entityName, $data);
} else { if ($entity !== null) {
foreach ($data as $field => $value) { $hints[Query::HINT_REFRESH] = true;
$this->_class->reflFields[$field]->setValue($entity, $value);
}
$id = array(); $id = array();
if ($this->_class->isIdentifierComposite) { if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) { foreach ($this->_class->identifier as $fieldName) {
...@@ -525,36 +524,7 @@ class StandardEntityPersister ...@@ -525,36 +524,7 @@ class StandardEntityPersister
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
} }
// Initialize associations return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints);
foreach ($this->_class->associationMappings as $field => $assoc) {
if ($assoc->isOneToOne()) {
if ($assoc->isLazilyFetched()) {
// Inject proxy
$proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnValues);
$this->_class->reflFields[$field]->setValue($entity, $proxy);
} else {
// Eager load
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumnValues);
}
} else {
// Inject collection
$coll = new PersistentCollection(
$this->_em,
$this->_em->getClassMetadata($assoc->targetEntityName),
/*$this->_class->reflFields[$field]->getValue($entity) ?:*/ new ArrayCollection);
$coll->setOwner($entity, $assoc);
$this->_class->reflFields[$field]->setValue($entity, $coll);
if ($assoc->isLazilyFetched()) {
$coll->setInitialized(false);
} else {
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, $coll, $this->_em);
}
}
}
return $entity;
} }
/** /**
...@@ -566,22 +536,23 @@ class StandardEntityPersister ...@@ -566,22 +536,23 @@ class StandardEntityPersister
protected function _getSelectEntitiesSql(array &$criteria, $assoc = null) protected function _getSelectEntitiesSql(array &$criteria, $assoc = null)
{ {
$columnList = ''; $columnList = '';
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) { foreach ($this->_class->fieldNames as $field) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_class->getQuotedColumnName($field, $this->_platform); $columnList .= $this->_class->getQuotedColumnName($field, $this->_platform);
} }
$joinColumnNames = array(); // Add join columns (foreign keys) to select list
foreach ($this->_class->associationMappings as $assoc2) { foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) { if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) { foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$joinColumnNames[] = $srcColumn;
$columnList .= ', ' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform); $columnList .= ', ' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform);
} }
} }
} }
$joinSql = ''; // Construct WHERE conditions
$conditionSql = ''; $conditionSql = '';
foreach ($criteria as $field => $value) { foreach ($criteria as $field => $value) {
if ($conditionSql != '') { if ($conditionSql != '') {
...@@ -600,7 +571,6 @@ class StandardEntityPersister ...@@ -600,7 +571,6 @@ class StandardEntityPersister
return 'SELECT ' . $columnList return 'SELECT ' . $columnList
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' FROM ' . $this->_class->getQuotedTableName($this->_platform)
. $joinSql
. ($conditionSql ? ' WHERE ' . $conditionSql : ''); . ($conditionSql ? ' WHERE ' . $conditionSql : '');
} }
......
...@@ -1653,7 +1653,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1653,7 +1653,7 @@ class UnitOfWork implements PropertyChangedListener
* @return object The created entity instance. * @return object The created entity instance.
* @internal Highly performance-sensitive method. * @internal Highly performance-sensitive method.
*/ */
public function createEntity($className, array $data, $hints = array()) public function createEntity($className, array $data, &$hints = array())
{ {
$class = $this->_em->getClassMetadata($className); $class = $this->_em->getClassMetadata($className);
...@@ -1695,9 +1695,53 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1695,9 +1695,53 @@ class UnitOfWork implements PropertyChangedListener
} }
} }
} }
// Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
foreach ($class->associationMappings as $field => $assoc) {
// Check if the association is not among the fetch-joined associatons already.
if ( ! isset($hints['fetched'][$className][$field])) {
if ($assoc->isOneToOne()) {
$joinColumns = array();
if ($assoc->isOwningSide) {
foreach ($assoc->targetToSourceKeyColumns as $srcColumn) {
$joinColumns[$srcColumn] = $data[$assoc->joinColumnFieldNames[$srcColumn]];
}
}
//TODO: If its in the identity map just get it from there if possible!
if ($assoc->isLazilyFetched() /*&& $assoc->isOwningSide)*/) {
// Inject proxy
$proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumns);
$this->_originalEntityData[$oid][$field] = $proxy;
$class->reflFields[$field]->setValue($entity, $proxy);
} else {
// Eager load
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumns);
}
} else {
// Inject collection
$reflField = $class->reflFields[$field];
$pColl = new PersistentCollection($this->_em,
$this->_em->getClassMetadata($assoc->targetEntityName),
$reflField->getValue($entity) ?: new ArrayCollection
);
$pColl->setOwner($entity, $assoc);
$reflField->setValue($entity, $pColl);
if ($assoc->isLazilyFetched()) {
$pColl->setInitialized(false);
} else {
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, $pColl, $this->_em);
}
$this->_originalEntityData[$oid][$field] = $pColl;
}
}
}
}
} }
//TODO: These should be invoked later, because associations are not yet loaded here. //TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
if (isset($class->lifecycleCallbacks[Events::postLoad])) { if (isset($class->lifecycleCallbacks[Events::postLoad])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity); $class->invokeLifecycleCallbacks(Events::postLoad, $entity);
} }
......
...@@ -339,7 +339,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase ...@@ -339,7 +339,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
* *
* MAXIMUM TIME: 1 second * MAXIMUM TIME: 1 second
*/ */
public function testMixedQueryFetchJoinFullObjectHydrationPerformance200Rows() public function testMixedQueryFetchJoinFullObjectHydrationPerformance2000Rows()
{ {
$rsm = new ResultSetMapping; $rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment