Skip to content

Commit

Permalink
Improoved single entity flush
Browse files Browse the repository at this point in the history
  • Loading branch information
goetas committed Sep 18, 2014
1 parent 170d692 commit f4b9fca
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 15 deletions.
59 changes: 46 additions & 13 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ class UnitOfWork implements PropertyChangedListener
*/
private $entityInsertions = array();

/**
* A list of all pending entity insertions, that can be avoided if we call
* a flush() method with a non related entity.
* @var array
*/
private $entityShouldBeInserted = array();

/**
* Used to detect if a flush is in progress.
* @see scheduleForInsert() method.
* @var boolean
*/
private $flushing = false;


/**
* A list of all pending entity updates.
*
Expand Down Expand Up @@ -301,6 +316,7 @@ public function __construct(EntityManager $em)
*/
public function commit($entity = null)
{
$this->flushing = true;
// Raise preFlush
if ($this->evm->hasListeners(Events::preFlush)) {
$this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em));
Expand All @@ -325,7 +341,7 @@ public function commit($entity = null)
$this->orphanRemovals)) {
$this->dispatchOnFlushEvent();
$this->dispatchPostFlushEvent();

$this->flushing = false;
return; // Nothing to do.
}

Expand Down Expand Up @@ -384,6 +400,7 @@ public function commit($entity = null)
$conn->rollback();

$this->afterTransactionRolledBack();
$this->flushing = false;

throw $e;
}
Expand All @@ -408,6 +425,7 @@ public function commit($entity = null)
$this->visitedCollections =
$this->scheduledForDirtyCheck =
$this->orphanRemovals = array();
$this->flushing = false;
}

/**
Expand Down Expand Up @@ -453,6 +471,11 @@ private function computeSingleEntityChangeSet($entity)
}

// Compute changes for INSERTed entities first. This must always happen even in this case.
$oid = spl_object_hash($entity);
if (isset($this->entityShouldBeInserted[$oid])) {
$this->entityInsertions[$oid] = $entity;
unset($this->entityShouldBeInserted[$oid]);
}
$this->computeScheduleInsertsChangeSets();

if ($class->isReadOnly) {
Expand Down Expand Up @@ -734,6 +757,9 @@ public function computeChangeSet(ClassMetadata $class, $entity)
public function computeChangeSets()
{
// Compute changes for INSERTed entities first. This must always happen.
$this->entityInsertions = $this->entityInsertions + $this->entityShouldBeInserted;
$this->entityShouldBeInserted = array();

$this->computeScheduleInsertsChangeSets();

// Compute changes for other MANAGED entities. Change tracking policies take effect here.
Expand Down Expand Up @@ -945,7 +971,7 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
if ($changeSet) {
if (isset($this->entityChangeSets[$oid])) {
$this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
} else if ( ! isset($this->entityInsertions[$oid])) {
} else if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityShouldBeInserted[$oid])) {
$this->entityChangeSets[$oid] = $changeSet;
$this->entityUpdates[$oid] = $entity;
}
Expand Down Expand Up @@ -975,7 +1001,7 @@ private function executeInserts($class)

$persister->addInsert($entity);

unset($this->entityInsertions[$oid]);
unset($this->entityInsertions[$oid], $this->entityShouldBeInserted[$oid]);

if ($invoke !== ListenersInvoker::INVOKE_NONE) {
$entities[] = $entity;
Expand Down Expand Up @@ -1177,15 +1203,19 @@ public function scheduleForInsert($entity)
if (isset($this->entityDeletions[$oid])) {
throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity);
}
if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) {
if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid]) && !isset($this->entityShouldBeInserted[$oid])) {
throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity);
}

if (isset($this->entityInsertions[$oid])) {
if (isset($this->entityInsertions[$oid]) || isset($this->entityShouldBeInserted[$oid])) {
throw ORMInvalidArgumentException::scheduleInsertTwice($entity);
}

$this->entityInsertions[$oid] = $entity;
if ($this->flushing) {
$this->entityInsertions[$oid] = $entity;
} else {
$this->entityShouldBeInserted[$oid] = $entity;
}

if (isset($this->entityIdentifiers[$oid])) {
$this->addToIdentityMap($entity);
Expand All @@ -1205,7 +1235,7 @@ public function scheduleForInsert($entity)
*/
public function isScheduledForInsert($entity)
{
return isset($this->entityInsertions[spl_object_hash($entity)]);
return isset($this->entityInsertions[spl_object_hash($entity)]) || isset($this->entityShouldBeInserted[spl_object_hash($entity)]);
}

/**
Expand All @@ -1229,7 +1259,7 @@ public function scheduleForUpdate($entity)
throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update");
}

if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) {
if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid]) && ! isset($this->entityShouldBeInserted[$oid])) {
$this->entityUpdates[$oid] = $entity;
}
}
Expand Down Expand Up @@ -1302,12 +1332,12 @@ public function scheduleForDelete($entity)
{
$oid = spl_object_hash($entity);

if (isset($this->entityInsertions[$oid])) {
if (isset($this->entityInsertions[$oid]) || isset($this->entityShouldBeInserted[$oid])) {
if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity);
}

unset($this->entityInsertions[$oid], $this->entityStates[$oid]);
unset($this->entityInsertions[$oid], $this->entityShouldBeInserted[$oid], $this->entityStates[$oid]);

return; // entity has not been persisted yet, so nothing more to do.
}
Expand Down Expand Up @@ -1354,7 +1384,8 @@ public function isEntityScheduled($entity)

return isset($this->entityInsertions[$oid])
|| isset($this->entityUpdates[$oid])
|| isset($this->entityDeletions[$oid]);
|| isset($this->entityDeletions[$oid])
|| isset($this->entityShouldBeInserted[$oid]);
}

/**
Expand Down Expand Up @@ -2016,7 +2047,8 @@ private function doDetach($entity, array &$visited, $noCascade = false)
$this->entityDeletions[$oid],
$this->entityIdentifiers[$oid],
$this->entityStates[$oid],
$this->originalEntityData[$oid]
$this->originalEntityData[$oid],
$this->entityShouldBeInserted[$oid]
);
break;
case self::STATE_NEW:
Expand Down Expand Up @@ -2391,6 +2423,7 @@ public function clear($entityName = null)
$this->entityChangeSets =
$this->entityStates =
$this->scheduledForDirtyCheck =
$this->entityShouldBeInserted =
$this->entityInsertions =
$this->entityUpdates =
$this->entityDeletions =
Expand Down Expand Up @@ -2995,7 +3028,7 @@ public function scheduleForDirtyCheck($entity)
*/
public function hasPendingInsertions()
{
return ! empty($this->entityInsertions);
return ! empty($this->entityInsertions) || !empty($this->entityShouldBeInserted);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ public function testFlushSingleAndNewEntity()
$this->_em->flush($user);

$this->assertTrue($this->_em->contains($otherUser), "Other user is contained in EntityManager");
$this->assertTrue($otherUser->id > 0, "other user has an id");
$this->assertFalse($otherUser->id > 0, "other user has an id");
}

/**
Expand Down Expand Up @@ -1294,7 +1294,7 @@ public function testProxyIsIgnored()
$this->_em->flush($user);

$this->assertTrue($this->_em->contains($otherUser), "Other user is contained in EntityManager");
$this->assertTrue($otherUser->id > 0, "other user has an id");
$this->assertFalse($otherUser->id > 0, "other user has an id");
}

/**
Expand Down

0 comments on commit f4b9fca

Please sign in to comment.