[2.0] Refactorings to reduce duplicated code and increase efficiency.
This commit is contained in:
parent
c54d582568
commit
e2d678cc10
@ -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');
|
||||
|
Loading…
x
Reference in New Issue
Block a user