[2.0] Initial version of optimistic locking with integer version columns
This commit is contained in:
parent
c9b0328279
commit
cc3ea569a4
@ -746,8 +746,9 @@ class Connection
|
|||||||
if ($this->_transactionNestingLevel == 1) {
|
if ($this->_transactionNestingLevel == 1) {
|
||||||
$this->_transactionNestingLevel = 0;
|
$this->_transactionNestingLevel = 0;
|
||||||
$this->_conn->rollback();
|
$this->_conn->rollback();
|
||||||
|
} else {
|
||||||
|
--$this->_transactionNestingLevel;
|
||||||
}
|
}
|
||||||
--$this->_transactionNestingLevel;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -850,7 +850,7 @@ abstract class AbstractPlatform
|
|||||||
$default = empty($field['notnull']) ? ' DEFAULT NULL' : '';
|
$default = empty($field['notnull']) ? ' DEFAULT NULL' : '';
|
||||||
|
|
||||||
if (isset($field['default'])) {
|
if (isset($field['default'])) {
|
||||||
$default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
|
$default = ' DEFAULT ' . $this->quoteIdentifier($field['default'], $field['type']);
|
||||||
}
|
}
|
||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ class EntityManager
|
|||||||
if ($this->_flushMode == self::FLUSHMODE_AUTO || $this->_flushMode == self::FLUSHMODE_COMMIT) {
|
if ($this->_flushMode == self::FLUSHMODE_AUTO || $this->_flushMode == self::FLUSHMODE_COMMIT) {
|
||||||
$this->flush();
|
$this->flush();
|
||||||
}
|
}
|
||||||
$this->_conn->commitTransaction();
|
$this->_conn->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,8 +35,8 @@ final class Events
|
|||||||
|
|
||||||
const preDelete = 'preDelete';
|
const preDelete = 'preDelete';
|
||||||
const postDelete = 'postDelete';
|
const postDelete = 'postDelete';
|
||||||
const preInsert = 'preSave';
|
const preInsert = 'preInsert';
|
||||||
const postInsert = 'postSave';
|
const postInsert = 'postInsert';
|
||||||
const preUpdate = 'preUpdate';
|
const preUpdate = 'preUpdate';
|
||||||
const postUpdate = 'postUpdate';
|
const postUpdate = 'postUpdate';
|
||||||
const load = 'load';
|
const load = 'load';
|
||||||
|
@ -397,6 +397,20 @@ final class ClassMetadata
|
|||||||
*/
|
*/
|
||||||
public $inheritedAssociationFields = array();
|
public $inheritedAssociationFields = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag for whether or not the model is to be versioned with optimistic locking
|
||||||
|
*
|
||||||
|
* @var boolean $isVersioned
|
||||||
|
*/
|
||||||
|
public $isVersioned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the field which stores the version information
|
||||||
|
*
|
||||||
|
* @var mixed $versionField
|
||||||
|
*/
|
||||||
|
public $versionField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
* Initializes a new ClassMetadata instance that will hold the object-relational mapping
|
||||||
* metadata of the class with the given name.
|
* metadata of the class with the given name.
|
||||||
@ -1758,6 +1772,24 @@ final class ClassMetadata
|
|||||||
$this->sequenceGeneratorDefinition = $definition;
|
$this->sequenceGeneratorDefinition = $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isVersioned($bool = null)
|
||||||
|
{
|
||||||
|
if ( ! is_null($bool)) {
|
||||||
|
$this->isVersioned = $bool;
|
||||||
|
}
|
||||||
|
return $this->isVersioned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersionField()
|
||||||
|
{
|
||||||
|
return $this->versionField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setVersionField($versionField)
|
||||||
|
{
|
||||||
|
$this->versionField = $versionField;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a string representation of this instance.
|
* Creates a string representation of this instance.
|
||||||
*
|
*
|
||||||
|
@ -162,6 +162,8 @@ class ClassMetadataFactory
|
|||||||
$this->_addInheritedFields($class, $parent);
|
$this->_addInheritedFields($class, $parent);
|
||||||
$this->_addInheritedRelations($class, $parent);
|
$this->_addInheritedRelations($class, $parent);
|
||||||
$class->setIdentifier($parent->identifier);
|
$class->setIdentifier($parent->identifier);
|
||||||
|
$class->isVersioned($parent->isVersioned);
|
||||||
|
$class->setVersionField($parent->versionField);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke driver
|
// Invoke driver
|
||||||
@ -259,13 +261,18 @@ class ClassMetadataFactory
|
|||||||
*/
|
*/
|
||||||
private function _generateStaticSql($class)
|
private function _generateStaticSql($class)
|
||||||
{
|
{
|
||||||
|
if ($versioned = $class->isVersioned()) {
|
||||||
|
$versionField = $class->getVersionField();
|
||||||
|
}
|
||||||
|
|
||||||
// Generate INSERT SQL
|
// Generate INSERT SQL
|
||||||
$columns = $values = array();
|
$columns = $values = array();
|
||||||
if ($class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_JOINED) {
|
if ($class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_JOINED) {
|
||||||
// Generate INSERT SQL for inheritance type JOINED
|
// Generate INSERT SQL for inheritance type JOINED
|
||||||
foreach ($class->reflFields as $name => $field) {
|
foreach ($class->reflFields as $name => $field) {
|
||||||
if (isset($class->fieldMappings[$name]['inherited']) && ! isset($class->fieldMappings[$name]['id'])
|
if (isset($class->fieldMappings[$name]['inherited']) && ! isset($class->fieldMappings[$name]['id'])
|
||||||
|| isset($class->inheritedAssociationFields[$name])) {
|
|| isset($class->inheritedAssociationFields[$name])
|
||||||
|
|| ($versioned && $versionField == $name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +292,9 @@ class ClassMetadataFactory
|
|||||||
} else {
|
} else {
|
||||||
// Generate INSERT SQL for inheritance types NONE, SINGLE_TABLE, TABLE_PER_CLASS
|
// Generate INSERT SQL for inheritance types NONE, SINGLE_TABLE, TABLE_PER_CLASS
|
||||||
foreach ($class->reflFields as $name => $field) {
|
foreach ($class->reflFields as $name => $field) {
|
||||||
|
if ($versioned && $versionField == $name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (isset($class->associationMappings[$name])) {
|
if (isset($class->associationMappings[$name])) {
|
||||||
$assoc = $class->associationMappings[$name];
|
$assoc = $class->associationMappings[$name];
|
||||||
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
|
if ($assoc->isOwningSide && $assoc->isOneToOne()) {
|
||||||
|
@ -144,6 +144,9 @@ class AnnotationDriver implements Driver
|
|||||||
$mapping['type'] = $columnAnnot->type;
|
$mapping['type'] = $columnAnnot->type;
|
||||||
$mapping['length'] = $columnAnnot->length;
|
$mapping['length'] = $columnAnnot->length;
|
||||||
$mapping['nullable'] = $columnAnnot->nullable;
|
$mapping['nullable'] = $columnAnnot->nullable;
|
||||||
|
if (isset($columnAnnot->default)) {
|
||||||
|
$mapping['default'] = $columnAnnot->default;
|
||||||
|
}
|
||||||
if (isset($columnAnnot->name)) {
|
if (isset($columnAnnot->name)) {
|
||||||
$mapping['columnName'] = $columnAnnot->name;
|
$mapping['columnName'] = $columnAnnot->name;
|
||||||
}
|
}
|
||||||
@ -153,6 +156,17 @@ class AnnotationDriver implements Driver
|
|||||||
if ($generatedValueAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) {
|
if ($generatedValueAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) {
|
||||||
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
|
$metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy));
|
||||||
}
|
}
|
||||||
|
if ($versionAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) {
|
||||||
|
$metadata->isVersioned(true);
|
||||||
|
$metadata->setVersionField($mapping['fieldName']);
|
||||||
|
|
||||||
|
if ( ! isset($mapping['default'])) {
|
||||||
|
// TODO: When we have timestamp optimistic locking
|
||||||
|
// we'll have to figure out a better way to do this?
|
||||||
|
// Can we set the default value to be NOW() ?
|
||||||
|
$mapping['default'] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
$metadata->mapField($mapping);
|
$metadata->mapField($mapping);
|
||||||
|
|
||||||
// Check for SequenceGenerator/TableGenerator definition
|
// Check for SequenceGenerator/TableGenerator definition
|
||||||
@ -165,7 +179,6 @@ class AnnotationDriver implements Driver
|
|||||||
} else if ($tblGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
|
} else if ($tblGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) {
|
||||||
throw new DoctrineException("DoctrineTableGenerator not yet implemented.");
|
throw new DoctrineException("DoctrineTableGenerator not yet implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
|
} else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) {
|
||||||
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
|
$mapping['targetEntity'] = $oneToOneAnnot->targetEntity;
|
||||||
$mapping['joinColumns'] = $joinColumns;
|
$mapping['joinColumns'] = $joinColumns;
|
||||||
|
@ -54,6 +54,7 @@ final class Column extends \Doctrine\Common\Annotations\Annotation {
|
|||||||
public $length;
|
public $length;
|
||||||
public $unique = false;
|
public $unique = false;
|
||||||
public $nullable = false;
|
public $nullable = false;
|
||||||
|
public $default;
|
||||||
public $name;
|
public $name;
|
||||||
}
|
}
|
||||||
final class OneToOne extends \Doctrine\Common\Annotations\Annotation {
|
final class OneToOne extends \Doctrine\Common\Annotations\Annotation {
|
||||||
@ -95,10 +96,9 @@ final class JoinTable extends \Doctrine\Common\Annotations\Annotation {
|
|||||||
public $inverseJoinColumns;
|
public $inverseJoinColumns;
|
||||||
}
|
}
|
||||||
final class SequenceGenerator extends \Doctrine\Common\Annotations\Annotation {
|
final class SequenceGenerator extends \Doctrine\Common\Annotations\Annotation {
|
||||||
//public $name;
|
|
||||||
public $sequenceName;
|
public $sequenceName;
|
||||||
public $allocationSize = 10;
|
public $allocationSize = 10;
|
||||||
public $initialValue = 1;
|
public $initialValue = 1;
|
||||||
}
|
}
|
||||||
final class ChangeTrackingPolicy extends \Doctrine\Common\Annotations\Annotation {}
|
final class ChangeTrackingPolicy extends \Doctrine\Common\Annotations\Annotation {}
|
||||||
final class DoctrineX extends \Doctrine\Common\Annotations\Annotation {}
|
final class DoctrineX extends \Doctrine\Common\Annotations\Annotation {}
|
35
lib/Doctrine/ORM/OptimisticLockException.php
Normal file
35
lib/Doctrine/ORM/OptimisticLockException.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the LGPL. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EntityManagerException
|
||||||
|
*
|
||||||
|
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||||
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link www.doctrine-project.org
|
||||||
|
* @since 2.0
|
||||||
|
* @version $Revision$
|
||||||
|
*/
|
||||||
|
class OptimisticLockException extends \Doctrine\Common\DoctrineException
|
||||||
|
{}
|
@ -94,6 +94,15 @@ class JoinedSubclassPersister extends StandardEntityPersister
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($isVersioned = $this->_class->isVersioned) {
|
||||||
|
if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) {
|
||||||
|
$definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited'];
|
||||||
|
$versionedClass = $this->_em->getClassMetadata($definingClassName);
|
||||||
|
} else {
|
||||||
|
$versionedClass = $this->_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$postInsertIds = array();
|
$postInsertIds = array();
|
||||||
$idGen = $this->_class->idGenerator;
|
$idGen = $this->_class->idGenerator;
|
||||||
$isPostInsertId = $idGen->isPostInsertGenerator();
|
$isPostInsertId = $idGen->isPostInsertGenerator();
|
||||||
@ -170,6 +179,10 @@ class JoinedSubclassPersister extends StandardEntityPersister
|
|||||||
foreach ($stmts as $stmt)
|
foreach ($stmts as $stmt)
|
||||||
$stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
|
|
||||||
|
if ($isVersioned) {
|
||||||
|
$this->_assignDefaultVersionValue($versionedClass, $entity, $id);
|
||||||
|
}
|
||||||
|
|
||||||
$this->_queuedInserts = array();
|
$this->_queuedInserts = array();
|
||||||
|
|
||||||
return $postInsertIds;
|
return $postInsertIds;
|
||||||
@ -192,7 +205,7 @@ class JoinedSubclassPersister extends StandardEntityPersister
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach ($updateData as $tableName => $data) {
|
foreach ($updateData as $tableName => $data) {
|
||||||
$this->_conn->update($tableName, $updateData[$tableName], $id);
|
$this->_doUpdate($entity, $tableName, $updateData[$tableName], $id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,8 @@ class StandardEntityPersister
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$isVersioned = $this->_class->isVersioned;
|
||||||
|
|
||||||
$postInsertIds = array();
|
$postInsertIds = array();
|
||||||
$idGen = $this->_class->idGenerator;
|
$idGen = $this->_class->idGenerator;
|
||||||
$isPostInsertId = $idGen->isPostInsertGenerator();
|
$isPostInsertId = $idGen->isPostInsertGenerator();
|
||||||
@ -159,7 +161,14 @@ class StandardEntityPersister
|
|||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|
||||||
if ($isPostInsertId) {
|
if ($isPostInsertId) {
|
||||||
$postInsertIds[$idGen->generate($this->_em, $entity)] = $entity;
|
$id = $idGen->generate($this->_em, $entity);
|
||||||
|
$postInsertIds[$id] = $entity;
|
||||||
|
} else {
|
||||||
|
$id = $this->_class->getIdentifierValues($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isVersioned) {
|
||||||
|
$this->_assignDefaultVersionValue($this->_class, $entity, $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($hasPostInsertListeners) {
|
if ($hasPostInsertListeners) {
|
||||||
@ -173,6 +182,18 @@ class StandardEntityPersister
|
|||||||
return $postInsertIds;
|
return $postInsertIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function _assignDefaultVersionValue($class, $entity, $id)
|
||||||
|
{
|
||||||
|
$versionField = $this->_class->getVersionField();
|
||||||
|
$identifier = $this->_class->getIdentifierColumnNames();
|
||||||
|
$versionFieldColumnName = $this->_class->getColumnName($versionField);
|
||||||
|
|
||||||
|
$sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->primaryTable['name'] .
|
||||||
|
" WHERE " . implode(' = ? AND ', $identifier) . " = ?";
|
||||||
|
$value = $this->_conn->fetchColumn($sql, (array) $id);
|
||||||
|
$this->_class->setFieldValue($entity, $versionField, $value[0]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an entity.
|
* Updates an entity.
|
||||||
*
|
*
|
||||||
@ -192,13 +213,44 @@ class StandardEntityPersister
|
|||||||
$this->_preUpdate($entity);
|
$this->_preUpdate($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_conn->update($tableName, $updateData[$tableName], $id);
|
if (isset($updateData[$tableName]) && $updateData[$tableName]) {
|
||||||
|
$this->_doUpdate($entity, $tableName, $updateData[$tableName], $id);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->_evm->hasListeners(Events::postUpdate)) {
|
if ($this->_evm->hasListeners(Events::postUpdate)) {
|
||||||
$this->_postUpdate($entity);
|
$this->_postUpdate($entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function _doUpdate($entity, $tableName, $data, $where)
|
||||||
|
{
|
||||||
|
$set = array();
|
||||||
|
foreach ($data as $columnName => $value) {
|
||||||
|
$set[] = $this->_conn->quoteIdentifier($columnName) . ' = ?';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($versioned = $this->_class->isVersioned()) {
|
||||||
|
$versionField = $this->_class->getVersionField();
|
||||||
|
$identifier = $this->_class->getIdentifier();
|
||||||
|
$versionFieldColumnName = $this->_class->getColumnName($versionField);
|
||||||
|
$where[$versionFieldColumnName] = $entity->version;
|
||||||
|
$set[] = $this->_conn->quoteIdentifier($versionFieldColumnName) . ' = ' .
|
||||||
|
$this->_conn->quoteIdentifier($versionFieldColumnName) . ' + 1';
|
||||||
|
}
|
||||||
|
$params = array_merge(array_values($data), array_values($where));
|
||||||
|
|
||||||
|
$sql = 'UPDATE ' . $this->_conn->quoteIdentifier($tableName)
|
||||||
|
. ' SET ' . implode(', ', $set)
|
||||||
|
. ' WHERE ' . implode(' = ? AND ', array_keys($where))
|
||||||
|
. ' = ?';
|
||||||
|
|
||||||
|
$result = $this->_conn->exec($sql, $params);
|
||||||
|
|
||||||
|
if ($versioned && ! $result) {
|
||||||
|
throw \Doctrine\ORM\OptimisticLockException::optimisticLockFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes an entity.
|
* Deletes an entity.
|
||||||
*
|
*
|
||||||
@ -269,7 +321,14 @@ class StandardEntityPersister
|
|||||||
$platform = $this->_conn->getDatabasePlatform();
|
$platform = $this->_conn->getDatabasePlatform();
|
||||||
$uow = $this->_em->getUnitOfWork();
|
$uow = $this->_em->getUnitOfWork();
|
||||||
|
|
||||||
|
if ($versioned = $this->_class->isVersioned()) {
|
||||||
|
$versionField = $this->_class->getVersionField();
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
|
foreach ($uow->getEntityChangeSet($entity) as $field => $change) {
|
||||||
|
if ($versioned && $versionField == $field) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$oldVal = $change[0];
|
$oldVal = $change[0];
|
||||||
$newVal = $change[1];
|
$newVal = $change[1];
|
||||||
|
|
||||||
|
@ -207,6 +207,9 @@ class SchemaTool
|
|||||||
$column['type'] = Type::getType($mapping['type']);
|
$column['type'] = Type::getType($mapping['type']);
|
||||||
$column['length'] = $mapping['length'];
|
$column['length'] = $mapping['length'];
|
||||||
$column['notnull'] = ! $mapping['nullable'];
|
$column['notnull'] = ! $mapping['nullable'];
|
||||||
|
if (isset($mapping['default'])) {
|
||||||
|
$column['default'] = $mapping['default'];
|
||||||
|
}
|
||||||
if ($class->isIdentifier($mapping['fieldName'])) {
|
if ($class->isIdentifier($mapping['fieldName'])) {
|
||||||
$column['primary'] = true;
|
$column['primary'] = true;
|
||||||
$options['primary'][] = $mapping['columnName'];
|
$options['primary'][] = $mapping['columnName'];
|
||||||
|
@ -42,6 +42,7 @@ class AllTests
|
|||||||
$suite->addTest(Mapping\AllTests::suite());
|
$suite->addTest(Mapping\AllTests::suite());
|
||||||
$suite->addTest(Functional\AllTests::suite());
|
$suite->addTest(Functional\AllTests::suite());
|
||||||
$suite->addTest(Id\AllTests::suite());
|
$suite->addTest(Id\AllTests::suite());
|
||||||
|
$suite->addTest(Locking\AllTests::suite());
|
||||||
|
|
||||||
return $suite;
|
return $suite;
|
||||||
}
|
}
|
||||||
|
30
tests/Doctrine/Tests/ORM/Locking/AllTests.php
Normal file
30
tests/Doctrine/Tests/ORM/Locking/AllTests.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM\Locking;
|
||||||
|
|
||||||
|
if (!defined('PHPUnit_MAIN_METHOD')) {
|
||||||
|
define('PHPUnit_MAIN_METHOD', 'Orm_Locking_AllTests::main');
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
|
class AllTests
|
||||||
|
{
|
||||||
|
public static function main()
|
||||||
|
{
|
||||||
|
\PHPUnit_TextUI_TestRunner::run(self::suite());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function suite()
|
||||||
|
{
|
||||||
|
$suite = new \Doctrine\Tests\DoctrineTestSuite('Doctrine Orm Locking');
|
||||||
|
|
||||||
|
$suite->addTestSuite('Doctrine\Tests\ORM\Locking\OptimisticTest');
|
||||||
|
|
||||||
|
return $suite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PHPUnit_MAIN_METHOD == 'Orm_Locking_AllTests::main') {
|
||||||
|
AllTests::main();
|
||||||
|
}
|
157
tests/Doctrine/Tests/ORM/Locking/OptimisticTest.php
Normal file
157
tests/Doctrine/Tests/ORM/Locking/OptimisticTest.php
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM\Locking;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Locking;
|
||||||
|
|
||||||
|
use Doctrine\Tests\Mocks\MetadataDriverMock;
|
||||||
|
use Doctrine\Tests\Mocks\DatabasePlatformMock;
|
||||||
|
use Doctrine\Tests\Mocks\EntityManagerMock;
|
||||||
|
use Doctrine\Tests\Mocks\ConnectionMock;
|
||||||
|
use Doctrine\Tests\Mocks\DriverMock;
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||||
|
use Doctrine\Common\EventManager;
|
||||||
|
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||||
|
use Doctrine\Tests\TestUtil;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
|
class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->_schemaTool->createSchema(array(
|
||||||
|
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Locking\OptimisticJoinedParent'),
|
||||||
|
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Locking\OptimisticJoinedChild'),
|
||||||
|
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Locking\OptimisticStandard')
|
||||||
|
));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Swallow all exceptions. We do not test the schema tool here.
|
||||||
|
}
|
||||||
|
$this->_conn = $this->_em->getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testJoinedInsertSetsInitialVersionValue()
|
||||||
|
{
|
||||||
|
$test = new OptimisticJoinedParent();
|
||||||
|
$test->name = 'test';
|
||||||
|
$this->_em->save($test);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$this->assertEquals(1, $test->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Doctrine\ORM\OptimisticLockException
|
||||||
|
*/
|
||||||
|
public function testJoinedFailureThrowsException()
|
||||||
|
{
|
||||||
|
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Locking\OptimisticJoinedParent t WHERE t.name = :name');
|
||||||
|
$q->setParameter('name', 'test');
|
||||||
|
$test = $q->getSingleResult();
|
||||||
|
|
||||||
|
// Manually update/increment the version so we can try and save the same
|
||||||
|
// $test and make sure the exception is thrown saying the record was
|
||||||
|
// changed or updated since you read it
|
||||||
|
$this->_conn->execute('UPDATE optimistic_joined_parent SET version = ? WHERE id = ?', array(2, $test->id));
|
||||||
|
|
||||||
|
// Now lets change a property and try and save it again
|
||||||
|
$test->name = 'WHATT???';
|
||||||
|
$this->_em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStandardInsertSetsInitialVersionValue()
|
||||||
|
{
|
||||||
|
$test = new OptimisticStandard();
|
||||||
|
$test->name = 'test';
|
||||||
|
$this->_em->save($test);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$this->assertEquals(1, $test->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Doctrine\ORM\OptimisticLockException
|
||||||
|
*/
|
||||||
|
public function testStandardFailureThrowsException()
|
||||||
|
{
|
||||||
|
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Locking\OptimisticStandard t WHERE t.name = :name');
|
||||||
|
$q->setParameter('name', 'test');
|
||||||
|
$test = $q->getSingleResult();
|
||||||
|
|
||||||
|
// Manually update/increment the version so we can try and save the same
|
||||||
|
// $test and make sure the exception is thrown saying the record was
|
||||||
|
// changed or updated since you read it
|
||||||
|
$this->_conn->execute('UPDATE optimistic_standard SET version = ? WHERE id = ?', array(2, $test->id));
|
||||||
|
|
||||||
|
// Now lets change a property and try and save it again
|
||||||
|
$test->name = 'WHATT???';
|
||||||
|
$this->_em->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
* @Table(name="optimistic_joined_parent")
|
||||||
|
* @DiscriminatorValue("parent")
|
||||||
|
* @InheritanceType("JOINED")
|
||||||
|
* @DiscriminatorColumn(name="discr", type="string")
|
||||||
|
* @SubClasses({"Doctrine\Tests\ORM\Locking\OptimisticJoinedChild"})
|
||||||
|
*/
|
||||||
|
class OptimisticJoinedParent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Id @Column(type="integer")
|
||||||
|
* @GeneratedValue(strategy="AUTO")
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Version @Column(type="integer")
|
||||||
|
*/
|
||||||
|
public $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
* @Table(name="optimistic_joined_child")
|
||||||
|
* @DiscriminatorValue("child")
|
||||||
|
*/
|
||||||
|
class OptimisticJoinedChild extends OptimisticJoinedParent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
public $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
* @Table(name="optimistic_standard")
|
||||||
|
*/
|
||||||
|
class OptimisticStandard
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Id @Column(type="integer")
|
||||||
|
* @GeneratedValue(strategy="AUTO")
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Version @Column(type="integer")
|
||||||
|
*/
|
||||||
|
public $version;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user