1
0
mirror of synced 2025-01-31 04:21:44 +03:00

[2.0][DDC-168] Moved INSERT SQL generation to persisters during runtime.

This commit is contained in:
romanb 2009-12-17 13:37:47 +00:00
parent a4d41d09ef
commit 3d14da4105
8 changed files with 168 additions and 106 deletions

View File

@ -36,6 +36,8 @@ use Doctrine\Common\EventManager,
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.org>
* @todo Remove flush modes. They dont seem to be of much use. Manual flushing should
* be enough.
*/
class EntityManager
{

View File

@ -333,7 +333,7 @@ final class ClassMetadata extends ClassMetadataInfo
'idGenerator',
'inheritanceType',
'inheritedAssociationFields',
'insertSql',
//'insertSql',
'inverseMappings', //TODO: Remove!
'isIdentifierComposite',
'isMappedSuperclass',

View File

@ -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.

View File

@ -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

View File

@ -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;
}
}

View File

@ -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)
{

View File

@ -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();

View File

@ -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);
}
}