diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index b0818cf4f..c8107082f 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -36,6 +36,8 @@ use Doctrine\Common\EventManager, * @since 2.0 * @version $Revision$ * @author Roman Borschel + * @todo Remove flush modes. They dont seem to be of much use. Manual flushing should + * be enough. */ class EntityManager { diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 6e0c60de3..3b44d4d8c 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -333,7 +333,7 @@ final class ClassMetadata extends ClassMetadataInfo 'idGenerator', 'inheritanceType', 'inheritedAssociationFields', - 'insertSql', + //'insertSql', 'inverseMappings', //TODO: Remove! 'isIdentifierComposite', 'isMappedSuperclass', diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 1da00b0ca..55330d9d4 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -254,10 +254,6 @@ class ClassMetadataFactory $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class); $this->_evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); } - - if ( ! $class->isMappedSuperclass) { - $this->_generateStaticSql($class); - } $this->_loadedMetadata[$className] = $class; @@ -324,82 +320,6 @@ class ClassMetadataFactory } } - /** - * Generates any static SQL strings for a class and stores them in the descriptor. - * - * @param ClassMetadata $class - */ - private function _generateStaticSql($class) - { - if ($versioned = $class->isVersioned) { - $versionField = $class->versionField; - } - - // Generate INSERT SQL - $columns = $values = array(); - if ($class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_JOINED) { - // Generate INSERT SQL for inheritance type JOINED - foreach ($class->reflFields as $name => $field) { - if (isset($class->fieldMappings[$name]['inherited']) && ! isset($class->fieldMappings[$name]['id']) - || isset($class->inheritedAssociationFields[$name]) - || ($versioned && $versionField == $name)) { - continue; - } - - if (isset($class->associationMappings[$name])) { - $assoc = $class->associationMappings[$name]; - if ($assoc->isOneToOne() && $assoc->isOwningSide) { - foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { - $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_targetPlatform); - } - } - } else if ($class->name != $class->rootEntityName || ! $class->isIdGeneratorIdentity() || $class->identifier[0] != $name) { - $columns[] = $class->getQuotedColumnName($name, $this->_targetPlatform); - } - } - } else { - // Generate INSERT SQL for inheritance types NONE, SINGLE_TABLE, TABLE_PER_CLASS - foreach ($class->reflFields as $name => $field) { - if ($versioned && $versionField == $name) { - continue; - } - if (isset($class->associationMappings[$name])) { - $assoc = $class->associationMappings[$name]; - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { - $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_targetPlatform); - } - } - } else if ($class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $class->identifier[0] != $name) { - $columns[] = $class->getQuotedColumnName($name, $this->_targetPlatform); - } - } - } - - // Add discriminator column to the INSERT SQL if necessary - if (isset($class->discriminatorColumn['name'])) { - if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined() - && $class->name == $class->rootEntityName) { - $columns[] = $class->getQuotedDiscriminatorColumnName($this->_targetPlatform); - } - } - - if (empty($columns)) { - $class->insertSql = $this->_targetPlatform->getEmptyIdentityInsertSql( - $class->getQuotedTableName($this->_targetPlatform), - $class->getQuotedColumnName($class->identifier[0], $this->_targetPlatform) - ); - } else { - $columns = array_unique($columns); - $values = array_fill(0, count($columns), '?'); - - $class->insertSql = 'INSERT INTO ' . - $class->getQuotedTableName($this->_targetPlatform) - . ' (' . implode(', ', $columns) . ') ' - . 'VALUES (' . implode(', ', $values) . ')'; - } - } - /** * Completes the ID generator mapping. If "auto" is specified we choose the generator * most appropriate for the targeted database platform. diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index feb3442a2..3ba8582c3 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -374,7 +374,7 @@ class ClassMetadataInfo * * @var string */ - public $insertSql; + //public $insertSql; /** * A map of field names to class names, where the field names are association diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index 3fecf8d8a..002beeccf 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -60,7 +60,7 @@ class JoinedSubclassPersister extends StandardEntityPersister * This function finds the ClassMetadata instance in a inheritance hierarchy * that is responsible for enabling versioning. * - * @return mixed $versionedClass ClassMetadata instance or false if versioning is not enabled + * @return mixed ClassMetadata instance or false if versioning is not enabled. */ private function _getVersionedClassMetadata() { @@ -90,13 +90,15 @@ class JoinedSubclassPersister extends StandardEntityPersister if (isset($this->_class->associationMappings[$fieldName])) { if (isset($this->_class->inheritedAssociationFields[$fieldName])) { $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( - $this->_class->inheritedAssociationFields[$fieldName])->primaryTable['name']; + $this->_class->inheritedAssociationFields[$fieldName] + )->primaryTable['name']; } else { $this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name']; } } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { $this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata( - $this->_class->fieldMappings[$fieldName]['inherited'])->primaryTable['name']; + $this->_class->fieldMappings[$fieldName]['inherited'] + )->primaryTable['name']; } else { $this->_owningTableMap[$fieldName] = $this->_class->primaryTable['name']; } @@ -122,16 +124,20 @@ class JoinedSubclassPersister extends StandardEntityPersister $postInsertIds = array(); $idGen = $this->_class->idGenerator; $isPostInsertId = $idGen->isPostInsertGenerator(); - $sqlLogger = $this->_conn->getConfiguration()->getSqlLogger(); // Prepare statements for all tables $stmts = $classes = array(); - $stmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->_class->insertSql); - $sql[$this->_class->primaryTable['name']] = $this->_class->insertSql; - foreach ($this->_class->parentClasses as $parentClass) { - $parentClass = $this->_em->getClassMetadata($parentClass); - $sql[$parentClass->primaryTable['name']] = $parentClass->insertSql; - $stmts[$parentClass->primaryTable['name']] = $this->_conn->prepare($parentClass->insertSql); + $stmts[$this->_class->primaryTable['name']] = $this->_conn->prepare($this->getInsertSql()); + if ($this->_sqlLogger !== null) { + $sql[$this->_class->primaryTable['name']] = $this->getInsertSql(); + } + foreach ($this->_class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); + $stmts[$parentClass->primaryTable['name']] = $this->_conn->prepare($parentPersister->getInsertSql()); + if ($this->_sqlLogger !== null) { + $sql[$parentClass->primaryTable['name']] = $parentPersister->getInsertSql(); + } } $rootTableName = $this->_em->getClassMetadata($this->_class->rootEntityName)->primaryTable['name']; @@ -142,13 +148,13 @@ class JoinedSubclassPersister extends StandardEntityPersister // Execute insert on root table $stmt = $stmts[$rootTableName]; $paramIndex = 1; - if ($sqlLogger) { + if ($this->_sqlLogger !== null) { $params = array(); foreach ($insertData[$rootTableName] as $columnName => $value) { $params[$paramIndex] = $value; $stmt->bindValue($paramIndex++, $value); } - $sqlLogger->logSql($sql[$rootTableName], $params); + $this->_sqlLogger->logSql($sql[$rootTableName], $params); } else { foreach ($insertData[$rootTableName] as $columnName => $value) { $stmt->bindValue($paramIndex++, $value); @@ -168,7 +174,7 @@ class JoinedSubclassPersister extends StandardEntityPersister foreach ($insertData as $tableName => $data) { $stmt = $stmts[$tableName]; $paramIndex = 1; - if ($sqlLogger) { + if ($this->_sqlLogger !== null) { $params = array(); foreach ((array) $id as $idVal) { $params[$paramIndex] = $idVal; @@ -178,7 +184,7 @@ class JoinedSubclassPersister extends StandardEntityPersister $params[$paramIndex] = $value; $stmt->bindValue($paramIndex++, $value); } - $sqlLogger->logSql($sql[$tableName], $params); + $this->_sqlLogger->logSql($sql[$tableName], $params); } else { foreach ((array) $id as $idVal) { $stmt->bindValue($paramIndex++, $idVal); @@ -406,4 +412,38 @@ class JoinedSubclassPersister extends StandardEntityPersister { return $this->_processSqlResultInheritanceAware($sqlResult); } + + /** @override */ + protected function _getInsertColumnList() + { + // Identifier columns must always come first in the column list of subclasses. + $columns = $this->_class->parentClasses ? $this->_class->getIdentifierColumnNames() : array(); + + foreach ($this->_class->reflFields as $name => $field) { + if (isset($this->_class->fieldMappings[$name]['inherited']) && ! isset($this->_class->fieldMappings[$name]['id']) + || isset($this->_class->inheritedAssociationFields[$name]) + || ($this->_class->isVersioned && $this->_class->versionField == $name)) { + continue; + } + + if (isset($this->_class->associationMappings[$name])) { + $assoc = $this->_class->associationMappings[$name]; + if ($assoc->isOneToOne() && $assoc->isOwningSide) { + foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { + $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_platform); + } + } + } else if ($this->_class->name != $this->_class->rootEntityName || + ! $this->_class->isIdGeneratorIdentity() || $this->_class->identifier[0] != $name) { + $columns[] = $this->_class->getQuotedColumnName($name, $this->_platform); + } + } + + // Add discriminator column if it is the topmost class. + if ($this->_class->name == $this->_class->rootEntityName) { + $columns[] = $this->_class->getQuotedDiscriminatorColumnName($this->_platform); + } + + return $columns; + } } diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index cb2cdc079..1ed289398 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -91,6 +91,16 @@ class SingleTablePersister extends StandardEntityPersister return $columnList; } + /** @override */ + protected function _getInsertColumnList() + { + $columns = parent::_getInsertColumnList(); + // Add discriminator column to the INSERT SQL + $columns[] = $this->_class->getQuotedDiscriminatorColumnName($this->_platform); + + return $columns; + } + /** @override */ protected function _processSqlResult(array $sqlResult) { diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index 2e863c70b..a7bfc12c9 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -87,7 +87,20 @@ class StandardEntityPersister */ protected $_queuedInserts = array(); + /** + * Mappings of column names as they appear in an SQL result set to + * column names as they are defined in the mapping. + * + * @var array + */ protected $_resultColumnNames = array(); + + /** + * The INSERT SQL statement used for entities handled by this persister. + * + * @var string + */ + private $_insertSql; /** * Initializes a new instance of a class derived from AbstractEntityPersister @@ -100,14 +113,15 @@ class StandardEntityPersister public function __construct(EntityManager $em, ClassMetadata $class) { $this->_em = $em; - $this->_conn = $em->getConnection(); - $this->_sqlLogger = $em->getConfiguration()->getSqlLogger(); - $this->_platform = $this->_conn->getDatabasePlatform(); $this->_class = $class; + $this->_conn = $em->getConnection(); + $this->_sqlLogger = $this->_conn->getConfiguration()->getSqlLogger(); + $this->_platform = $this->_conn->getDatabasePlatform(); } /** * Adds an entity to the queued insertions. + * The entity remains queued until {@link executeInserts()} is invoked. * * @param object $entity The entitiy to queue for insertion. */ @@ -133,24 +147,22 @@ class StandardEntityPersister $idGen = $this->_class->idGenerator; $isPostInsertId = $idGen->isPostInsertGenerator(); - $stmt = $this->_conn->prepare($this->_class->insertSql); + $stmt = $this->_conn->prepare($this->getInsertSql()); $primaryTableName = $this->_class->primaryTable['name']; - $sqlLogger = $this->_conn->getConfiguration()->getSqlLogger(); - foreach ($this->_queuedInserts as $entity) { $insertData = array(); $this->_prepareData($entity, $insertData, true); if (isset($insertData[$primaryTableName])) { $paramIndex = 1; - if ($sqlLogger) { + if ($this->_sqlLogger !== null) { $params = array(); foreach ($insertData[$primaryTableName] as $value) { $params[$paramIndex] = $value; $stmt->bindValue($paramIndex++, $value); } - $sqlLogger->logSql($this->_class->insertSql, $params); + $this->_sqlLogger->logSql($this->getInsertSql(), $params); } else { foreach ($insertData[$primaryTableName] as $value) { $stmt->bindValue($paramIndex++, $value); @@ -787,6 +799,74 @@ class StandardEntityPersister return array($entityName, $data); } + /** + * Gets the INSERT SQL used by the persister to persist entities. + * + * @return string + */ + public function getInsertSql() + { + if ($this->_insertSql === null) { + $this->_insertSql = $this->_generateInsertSql(); + } + + return $this->_insertSql; + } + + /** + * Gets the list of columns to put in the INSERT SQL statement. + * + * @return array The list of columns. + */ + protected function _getInsertColumnList() + { + $columns = array(); + foreach ($this->_class->reflFields as $name => $field) { + if ($this->_class->isVersioned && $this->_class->versionField == $name) { + continue; + } + if (isset($this->_class->associationMappings[$name])) { + $assoc = $this->_class->associationMappings[$name]; + if ($assoc->isOwningSide && $assoc->isOneToOne()) { + foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { + $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_platform); + } + } + } else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || + $this->_class->identifier[0] != $name) { + $columns[] = $this->_class->getQuotedColumnName($name, $this->_platform); + } + } + + return $columns; + } + + /** + * Generates the INSERT SQL used by the persister to persist entities. + * + * @return string + */ + protected function _generateInsertSql() + { + $insertSql = ''; + $columns = $this->_getInsertColumnList(); + if (empty($columns)) { + $insertSql = $this->_platform->getEmptyIdentityInsertSql( + $this->_class->getQuotedTableName($this->_platform), + $this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform) + ); + } else { + $columns = array_unique($columns); + $values = array_fill(0, count($columns), '?'); + + $insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform) + . ' (' . implode(', ', $columns) . ') ' + . 'VALUES (' . implode(', ', $values) . ')'; + } + + return $insertSql; + } + private function _findDeclaringClass($column) { static $cache = array(); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php index 0e4f0f2d5..b9ec0c406 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC168Test.php @@ -2,6 +2,8 @@ namespace Doctrine\Tests\ORM\Functional\Ticket; +use Doctrine\Tests\Models\Company\CompanyEmployee; + require_once __DIR__ . '/../../../TestInit.php'; class DDC168Test extends \Doctrine\Tests\OrmFunctionalTestCase @@ -16,13 +18,17 @@ class DDC168Test extends \Doctrine\Tests\OrmFunctionalTestCase */ public function testJoinedSubclassPersisterRequiresSpecificOrderOfMetadataReflFieldsArray() { + //$this->_em->getConnection()->getConfiguration()->setSqlLogger(new \Doctrine\DBAL\Logging\EchoSqlLogger); + $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\Company\CompanyEmployee'); ksort($metadata->reflFields); - $spouse = new \Doctrine\Tests\Models\Company\CompanyEmployee(); + $spouse = new CompanyEmployee; $spouse->setName("Blub"); + $spouse->setDepartment("Accounting"); + $spouse->setSalary(500); - $employee = new \Doctrine\Tests\Models\Company\CompanyEmployee(); + $employee = new CompanyEmployee; $employee->setName("Foo"); $employee->setDepartment("bar"); $employee->setSalary(1000); @@ -39,5 +45,9 @@ class DDC168Test extends \Doctrine\Tests\OrmFunctionalTestCase $theEmployee = $q->getSingleResult(); $this->assertEquals("bar", $theEmployee->getDepartment()); + $this->assertEquals("Foo", $theEmployee->getName()); + $this->assertEquals(1000, $theEmployee->getSalary()); + $this->assertTrue($theEmployee instanceof CompanyEmployee); + $this->assertTrue($theEmployee->getSpouse() instanceof CompanyEmployee); } } \ No newline at end of file