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
/* The following parts are reinitialized on every hydration run. */
private $_allowPartialObjects = false;
private $_identifierMap;
private $_resultPointers;
private $_idTemplate;
private $_resultCounter;
private $_fetchedAssociations;
private $_rootAliases = array();
private $_initializedCollections = array();
private $_existingCollections = array();
private $_proxyFactory;
//private $_createdEntities;
/** @override */
protected function _prepare()
{
$this->_allowPartialObjects = isset($this->_hints[Query::HINT_FORCE_PARTIAL_LOAD]);
$this->_proxyFactory = $this->_em->getProxyFactory();
$this->_identifierMap =
$this->_resultPointers =
$this->_idTemplate =
$this->_fetchedAssociations = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
......@@ -85,13 +77,13 @@ class ObjectHydrator extends AbstractHydrator
$targetClass = $this->_getClassMetadata($targetClassName);
$this->_ce[$targetClassName] = $targetClass;
$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) {
$this->_fetchedAssociations[$assoc->targetEntityName][$assoc->mappedByFieldName] = true;
$this->_hints['fetched'][$assoc->targetEntityName][$assoc->mappedByFieldName] = true;
} else {
if (isset($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
unset($data[$discrColumn]);
}
$entity = $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;
return $this->_uow->createEntity($className, $data, $this->_hints);
}
private function _getEntityFromIdentityMap($className, array $data)
......
......@@ -185,7 +185,8 @@ class ClassMetadataFactory
$class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField);
$class->setDiscriminatorMap($parent->discriminatorMap);
$class->setDiscriminatorMap($parent->discriminatorMap);
$class->resultColumnNames = $parent->resultColumnNames;
}
// Invoke driver
......
......@@ -282,6 +282,7 @@ class JoinedSubclassPersister extends StandardEntityPersister
$tableAliases[$className] = 't' . $aliasIndex++;
}
// Add regular columns
$columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
$tableAlias = isset($mapping['inherited']) ?
......@@ -290,6 +291,15 @@ class JoinedSubclassPersister extends StandardEntityPersister
$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
if ($this->_class->rootEntityName == $this->_class->name) {
$columnList .= ', ' . $baseTableAlias . '.' .
......@@ -315,6 +325,7 @@ class JoinedSubclassPersister extends StandardEntityPersister
// OUTER JOIN sub tables
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);
$tableAlias = $tableAliases[$subClassName];
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
......
......@@ -27,6 +27,7 @@ use Doctrine\Common\DoctrineException,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager,
Doctrine\ORM\UnitOfWork,
Doctrine\ORM\Query,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Events;
......@@ -236,9 +237,8 @@ class StandardEntityPersister
if ($isVersioned = $this->_class->isVersioned) {
$versionField = $this->_class->versionField;
$versionFieldType = $this->_class->getTypeOfField($versionField);
$where[$this->_class->fieldNames[$versionField]] = Type::getType(
$this->_class->fieldMappings[$versionField]['type']
)->convertToDatabaseValue($entity->version, $this->_platform);
$where[$versionField] = Type::getType($versionFieldType)
->convertToDatabaseValue($entity->version, $this->_platform);
$versionFieldColumnName = $this->_class->getQuotedColumnName($versionField, $this->_platform);
if ($versionFieldType == 'integer') {
$set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1';
......@@ -504,16 +504,15 @@ class StandardEntityPersister
} else if ($this->_class->discriminatorColumn !== null && $column == $this->_class->discriminatorColumn['name']) {
$entityName = $this->_class->discriminatorMap[$value];
} else {
$data[$column] = $value;
$joinColumnValues[$column] = $value;
}
}
if ($entity === null) {
$entity = $this->_em->getUnitOfWork()->createEntity($entityName, $data);
} else {
foreach ($data as $field => $value) {
$this->_class->reflFields[$field]->setValue($entity, $value);
}
$hints = array();
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
$id = array();
if ($this->_class->isIdentifierComposite) {
foreach ($this->_class->identifier as $fieldName) {
......@@ -524,37 +523,8 @@ class StandardEntityPersister
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
// Initialize associations
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;
return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints);
}
/**
......@@ -566,22 +536,23 @@ class StandardEntityPersister
protected function _getSelectEntitiesSql(array &$criteria, $assoc = null)
{
$columnList = '';
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_class->getQuotedColumnName($field, $this->_platform);
}
$joinColumnNames = array();
// Add join columns (foreign keys) to select list
foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2->isOwningSide && $assoc2->isOneToOne()) {
foreach ($assoc2->targetToSourceKeyColumns as $srcColumn) {
$joinColumnNames[] = $srcColumn;
$columnList .= ', ' . $assoc2->getQuotedJoinColumnName($srcColumn, $this->_platform);
}
}
}
$joinSql = '';
// Construct WHERE conditions
$conditionSql = '';
foreach ($criteria as $field => $value) {
if ($conditionSql != '') {
......@@ -600,7 +571,6 @@ class StandardEntityPersister
return 'SELECT ' . $columnList
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform)
. $joinSql
. ($conditionSql ? ' WHERE ' . $conditionSql : '');
}
......
......@@ -1653,7 +1653,7 @@ class UnitOfWork implements PropertyChangedListener
* @return object The created entity instance.
* @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);
......@@ -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])) {
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
}
......
......@@ -339,7 +339,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
*
* MAXIMUM TIME: 1 second
*/
public function testMixedQueryFetchJoinFullObjectHydrationPerformance200Rows()
public function testMixedQueryFetchJoinFullObjectHydrationPerformance2000Rows()
{
$rsm = new ResultSetMapping;
$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