Commit 4f5b332d authored by romanb's avatar romanb

[2.0] Adding insert performance tests.

parent c07416ac
......@@ -515,7 +515,7 @@ class Connection
{
$this->connect();
try {
echo "DBAL:" . $query . PHP_EOL;
//echo "DBAL:" . $query . PHP_EOL;
if ( ! empty($params)) {
$stmt = $this->prepare($query);
$stmt->execute($params);
......@@ -542,9 +542,9 @@ class Connection
public function exec($query, array $params = array()) {
$this->connect();
try {
echo $query . PHP_EOL;
//echo $query . PHP_EOL;
if ( ! empty($params)) {
var_dump($params);
//var_dump($params);
$stmt = $this->prepare($query);
$stmt->execute($params);
return $stmt->rowCount();
......
......@@ -346,7 +346,7 @@ class EntityManager
public function clear($entityName = null)
{
if ($entityName === null) {
$this->_unitOfWork->detachAll();
$this->_unitOfWork->clear();
} else {
//TODO
throw DoctrineException::notImplemented();
......
......@@ -360,8 +360,6 @@ final class ClassMetadata
*/
public $reflFields;
//private $_insertSql;
/**
* The ID generator used for generating IDs for this class.
*
......@@ -392,6 +390,13 @@ final class ClassMetadata
*/
public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
/**
* The SQL INSERT string for entities of this class.
*
* @var string
*/
public $insertSql;
/**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
* metadata of the class with the given name.
......@@ -1373,6 +1378,8 @@ final class ClassMetadata
public function addFieldMapping(array $fieldMapping)
{
$this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
$this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
$this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
}
/**
......@@ -1754,6 +1761,46 @@ final class ClassMetadata
$this->sequenceGeneratorDefinition = $definition;
}
/**
* INTERNAL: Called by ClassMetadataFactory.
*
* Tells this class descriptor to finish the mapping definition, making any
* final adjustments, i.e. generating some SQL strings.
*/
public function finishMapping()
{
$columns = $values = array();
if ($this->inheritanceType == self::INHERITANCE_TYPE_JOINED) {
//TODO
} else {
foreach ($this->reflFields as $name => $field) {
if (isset($this->associationMappings[$name])) {
$assoc = $this->associationMappings[$name];
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
foreach ($assoc->targetToSourceKeyColumns as $sourceCol) {
$columns[] = $sourceCol;
$values[] = '?';
}
}
} else if ($this->generatorType != self::GENERATOR_TYPE_IDENTITY || $this->identifier[0] != $name) {
$columns[] = $this->columnNames[$name];
$values[] = '?';
}
}
}
if ($this->discriminatorColumn) {
$columns[] = $this->discriminatorColumn['name'];
$values[] = '?';
}
$this->insertSql = 'INSERT INTO ' . $this->primaryTable['name']
. ' (' . implode(', ', $columns) . ') '
. 'VALUES (' . implode(', ', $values) . ')';
}
/**
* Creates a string representation of this instance.
*
......
......@@ -136,7 +136,6 @@ class ClassMetadataFactory
$class = $this->_newClassMetadataInstance($className);
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
//$class->setDiscriminatorMap($parent->getDiscriminatorMap());
$class->setDiscriminatorColumn($parent->discriminatorColumn);
$class->setIdGeneratorType($parent->generatorType);
$this->_addInheritedFields($class, $parent);
......@@ -167,9 +166,12 @@ class ClassMetadataFactory
$class->setTableName($parent->getTableName());
}
$class->setParentClasses($visited);
$class->finishMapping();
$this->_loadedMetadata[$className] = $class;
$parent = $class;
$class->setParentClasses($visited);
array_unshift($visited, $className);
}
}
......
......@@ -96,7 +96,11 @@ class StandardEntityPersister
{
$insertData = array();
$this->_prepareData($entity, $insertData, true);
$this->_conn->insert($this->_class->getTableName(), $insertData);
$stmt = $this->_conn->prepare($this->_class->insertSql);
$stmt->execute(array_values($insertData));
$stmt->closeCursor();
$idGen = $this->_class->getIdGenerator();
if ($idGen->isPostInsertGenerator()) {
return $idGen->generate($this->_em, $entity);
......@@ -111,9 +115,7 @@ class StandardEntityPersister
*/
public function addInsert($entity)
{
$insertData = array();
$this->_prepareData($entity, $insertData, true);
$this->_queuedInserts[] = $insertData;
$this->_queuedInserts[] = $entity;
}
/**
......@@ -121,12 +123,27 @@ class StandardEntityPersister
*/
public function executeInserts()
{
//$tableName = $this->_class->getTableName();
$stmt = $this->_conn->prepare($this->_class->getInsertSql());
foreach ($this->_queuedInserts as $insertData) {
if ( ! $this->_queuedInserts) {
return;
}
$postInsertIds = array();
$idGen = $this->_class->getIdGenerator();
$isPostInsertId = $idGen->isPostInsertGenerator();
$stmt = $this->_conn->prepare($this->_class->insertSql);
foreach ($this->_queuedInserts as $entity) {
$insertData = array();
$this->_prepareData($entity, $insertData, true);
$stmt->execute(array_values($insertData));
if ($isPostInsertId) {
$postInsertIds[$idGen->generate($this->_em, $entity)] = $entity;
}
}
$stmt->closeCursor();
$this->_queuedInserts = array();
return $postInsertIds;
}
/**
......@@ -226,13 +243,13 @@ class StandardEntityPersister
$columnName = $this->_class->getColumnName($field);
if ($this->_class->hasAssociation($field)) {
$assocMapping = $this->_class->getAssociationMapping($field);
if (isset($this->_class->associationMappings[$field])) {
$assocMapping = $this->_class->associationMappings[$field];
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
continue;
}
foreach ($assocMapping->getSourceToTargetKeyColumns() as $sourceColumn => $targetColumn) {
$otherClass = $this->_em->getClassMetadata($assocMapping->getTargetEntityName());
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
if ($newVal === null) {
$result[$sourceColumn] = null;
} else {
......
......@@ -235,7 +235,9 @@ class UnitOfWork implements PropertyChangedListener
// Now we need a commit order to maintain referential integrity
$commitOrder = $this->_getCommitOrder();
//TODO: begin transaction here?
$conn = $this->_em->getConnection();
try {
$conn->beginTransaction();
foreach ($commitOrder as $class) {
$this->_executeInserts($class);
......@@ -261,6 +263,12 @@ class UnitOfWork implements PropertyChangedListener
$this->_executeDeletions($commitOrder[$i]);
}
$conn->commit();
} catch (\Exception $e) {
$conn->rollback();
throw $e;
}
//TODO: commit transaction here?
// Take new snapshots from visited collections
......@@ -401,7 +409,7 @@ class UnitOfWork implements PropertyChangedListener
$assoc = $class->getAssociationMapping($name);
//echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL;
// Inject PersistentCollection
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->getTargetEntityName()),
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName),
$actualData[$name] ? $actualData[$name] : array());
$coll->setOwner($entity, $assoc);
if ( ! $coll->isEmpty()) {
......@@ -438,7 +446,7 @@ class UnitOfWork implements PropertyChangedListener
if (isset($changeSet[$propName])) {
if ($class->hasAssociation($propName)) {
$assoc = $class->getAssociationMapping($propName);
if ($assoc->isOneToOne() && $assoc->isOwningSide()) {
if ($assoc->isOneToOne() && $assoc->isOwningSide) {
$entityIsDirty = true;
} else if ($orgValue instanceof PersistentCollection) {
// A PersistentCollection was de-referenced, so delete it.
......@@ -538,28 +546,27 @@ class UnitOfWork implements PropertyChangedListener
*/
private function _executeInserts($class)
{
//TODO: Maybe $persister->addInsert($entity) in the loop and
// $persister->executeInserts() at the end to allow easy prepared
// statement reuse and maybe bulk operations in the persister.
// Same for update/delete.
$className = $class->name;
$persister = $this->getEntityPersister($className);
foreach ($this->_entityInsertions as $entity) {
if (get_class($entity) == $className) {
$returnVal = $persister->insert($entity);
if ($returnVal !== null) {
$persister->addInsert($entity);
}
}
$postInsertIds = $persister->executeInserts();
if ($postInsertIds) {
foreach ($postInsertIds as $id => $entity) {
// Persister returned a post-insert ID
$oid = spl_object_hash($entity);
$idField = $class->getSingleIdentifierFieldName();
$class->reflFields[$idField]->setValue($entity, $returnVal);
$this->_entityIdentifiers[$oid] = array($returnVal);
$idField = $class->identifier[0];
$class->reflFields[$idField]->setValue($entity, $id);
$this->_entityIdentifiers[$oid] = array($id);
$this->_entityStates[$oid] = self::STATE_MANAGED;
$this->_originalEntityData[$oid][$idField] = $returnVal;
$this->_originalEntityData[$oid][$idField] = $id;
$this->addToIdentityMap($entity);
}
}
}
}
/**
* Executes all entity updates for entities of the specified type.
......
......@@ -12,6 +12,7 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\StandardEntityPersist
private $_deletes = array();
private $_identityColumnValueCounter = 0;
private $_mockIdGeneratorType;
private $_postInsertIds = array();
/**
* @param <type> $entity
......@@ -22,12 +23,31 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\StandardEntityPersist
{
$this->_inserts[] = $entity;
if ( ! is_null($this->_mockIdGeneratorType) && $this->_mockIdGeneratorType == \Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY
|| $this->_classMetadata->isIdGeneratorIdentity()) {
return $this->_identityColumnValueCounter++;
|| $this->_class->isIdGeneratorIdentity()) {
$id = $this->_identityColumnValueCounter++;
$this->_postInsertIds[$id] = $entity;
return $id;
}
return null;
}
public function addInsert($entity)
{
$this->_inserts[] = $entity;
if ( ! is_null($this->_mockIdGeneratorType) && $this->_mockIdGeneratorType == \Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY
|| $this->_class->isIdGeneratorIdentity()) {
$id = $this->_identityColumnValueCounter++;
$this->_postInsertIds[$id] = $entity;
return $id;
}
return null;
}
public function executeInserts()
{
return $this->_postInsertIds;
}
public function setMockIdGeneratorType($genType)
{
$this->_mockIdGeneratorType = $genType;
......
......@@ -30,7 +30,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\UnitOfWorkTest');
$suite->addTestSuite('Doctrine\Tests\ORM\EntityManagerTest');
$suite->addTestSuite('Doctrine\Tests\ORM\EntityPersisterTest');
//$suite->addTestSuite('Doctrine\Tests\ORM\EntityPersisterTest');
$suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest');
$suite->addTest(Query\AllTests::suite());
......
......@@ -21,6 +21,7 @@ class AllTests
$suite = new \Doctrine\Tests\DoctrineTestSuite('Doctrine Orm Performance');
$suite->addTestSuite('Doctrine\Tests\ORM\Performance\HydrationPerformanceTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Performance\InsertPerformanceTest');
return $suite;
}
......
<?php
namespace Doctrine\Tests\ORM\Performance;
require_once __DIR__ . '/../../TestInit.php';
use Doctrine\Tests\Models\CMS\CmsUser;
/**
* Description of InsertPerformanceTest
*
* @author robo
*/
class InsertPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase
{
protected function setUp() {
$this->useModelSet('cms');
parent::setUp();
}
/**
* [romanb: 10000 objects in ~8 seconds]
*/
public function testInsertPerformance()
{
$s = microtime(true);
$conn = $this->_em->getConnection();
$this->setMaxRunningTime(10);
//$mem = memory_get_usage();
//echo "Memory usage before: " . ($mem / 1024) . " KB" . PHP_EOL;
for ($i=0; $i<10000; ++$i) {
$user = new CmsUser;
$user->status = 'user';
$user->username = 'user' . $i;
$user->name = 'Mr.Smith-' . $i;
$this->_em->save($user);
if (($i % 20) == 0) {
$this->_em->flush();
$this->_em->clear();
}
}
//$memAfter = memory_get_usage();
//echo "Memory usage after: " . ($memAfter / 1024) . " KB" . PHP_EOL;
$e = microtime(true);
echo ' Inserted 10000 records in ' . ($e - $s) . ' seconds' . PHP_EOL;
}
}
......@@ -125,6 +125,9 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
public function testChangeTrackingNotify()
{
$persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\ORM\NotifyChangedEntity"));
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedEntity', $persister);
$entity = new NotifyChangedEntity;
$entity->setData('thedata');
$this->_unitOfWork->save($entity);
......
......@@ -7,16 +7,8 @@ namespace Doctrine\Tests;
*
* @author robo
*/
class OrmPerformanceTestCase extends OrmTestCase
class OrmPerformanceTestCase extends OrmFunctionalTestCase
{
protected $_em;
protected function setUp()
{
parent::setUp();
$this->_em = $this->_getTestEntityManager();
}
/**
* @var integer
*/
......
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