From 05d5fe49540386c527d2adf4a4f87587f83f83e2 Mon Sep 17 00:00:00 2001 From: jwage Date: Mon, 24 Aug 2009 17:06:12 +0000 Subject: [PATCH] [2.0] Finishing optimistic locking with timestamp support --- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 23 ++++++++ .../ORM/Mapping/Driver/AnnotationDriver.php | 10 +--- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 3 ++ .../ORM/Mapping/Driver/YamlDriver.php | 3 ++ .../Persisters/StandardEntityPersister.php | 11 +++- .../ORM/Functional/Locking/OptimisticTest.php | 53 ++++++++++++++++++- 6 files changed, 91 insertions(+), 12 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index cbc8bf629..646dcb082 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -1742,6 +1742,29 @@ final class ClassMetadata $this->sequenceGeneratorDefinition = $definition; } + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type + * + * @param array $mapping The version field mapping array + * @return void + */ + public function setVersionMapping(array &$mapping) + { + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + + if ( ! isset($mapping['default'])) { + if ($mapping['type'] == 'integer') { + $mapping['default'] = 1; + } else if ($mapping['type'] == 'datetime') { + $mapping['default'] = 'CURRENT_TIMESTAMP'; + } else { + throw DoctrineException::unsupportedOptimisticLockingType($mapping['type']); + } + } + } + /** * Checks whether this class is versioned for optimistic locking. * diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index d962bd495..703a68add 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -179,15 +179,7 @@ class AnnotationDriver implements Driver $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy)); } if ($versionAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) { - $metadata->setVersioned(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->setVersionMapping($mapping); } $metadata->mapField($mapping); diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 6e26b8ae7..fc3a7942f 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -90,6 +90,9 @@ class XmlDriver extends AbstractFileDriver if (isset($fieldMapping['scale'])) { $mapping['scale'] = (int)$fieldMapping['scale']; } + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $metadata->setVersionMapping($mapping); + } $metadata->mapField($mapping); } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index 9552c2edd..1de744ad5 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -70,6 +70,9 @@ class YamlDriver extends AbstractFileDriver if (isset($fieldMapping['length'])) { $mapping['length'] = $fieldMapping['length']; } + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $metadata->setVersionMapping($mapping); + } $metadata->mapField($mapping); } } diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index debf3cc46..824316c52 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -233,9 +233,16 @@ class StandardEntityPersister if ($isVersioned = $this->_class->isVersioned) { $versionField = $this->_class->versionField; - $where[$this->_class->fieldNames[$versionField]] = $entity->version; + $versionFieldType = $this->_class->getTypeOfField($versionField); + $where[$this->_class->fieldNames[$versionField]] = Type::getType( + $this->_class->fieldMappings[$versionField]['type'] + )->convertToDatabaseValue($entity->version, $this->_platform); $versionFieldColumnName = $this->_class->getQuotedColumnName($versionField, $this->_platform); - $set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1'; + if ($versionFieldType == 'integer') { + $set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1'; + } else if ($versionFieldType == 'datetime') { + $set[] = $versionFieldColumnName . ' = CURRENT_TIMESTAMP'; + } } $params = array_merge(array_values($data), array_values($where)); diff --git a/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php b/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php index fc9042904..8fb7c4f1a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/Locking/OptimisticTest.php @@ -24,7 +24,8 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_schemaTool->createSchema(array( $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent'), $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild'), - $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard') + $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard'), + $this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp') )); } catch (\Exception $e) { // Swallow all exceptions. We do not test the schema tool here. @@ -119,6 +120,33 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase $test->name = 'WHATT???'; $this->_em->flush(); } + + public function testOptimisticTimestampSetsDefaultValue() + { + $test = new OptimisticTimestamp(); + $test->name = 'Testing'; + $this->_em->persist($test); + $this->_em->flush(); + + $this->assertTrue(strtotime($test->version) > 0); + } + + /** + * @expectedException Doctrine\ORM\OptimisticLockException + */ + public function testOptimisticTimestampFailureThrowsException() + { + $q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.name = :name'); + $q->setParameter('name', 'Testing'); + $test = $q->getSingleResult(); + + // Manually increment the version datetime column + $this->_conn->execute('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', array(date('Y-m-d H:i:s', strtotime($test->version->format('Y-m-d H:i:s')) + 3600), $test->id)); + + // Try and update the record and it should throw an exception + $test->name = 'Testing again'; + $this->_em->flush(); + } } /** @@ -180,4 +208,27 @@ class OptimisticStandard * @Version @Column(type="integer") */ public $version; +} + +/** + * @Entity + * @Table(name="optimistic_timestamp") + */ +class OptimisticTimestamp +{ + /** + * @Id @Column(type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $id; + + /** + * @Column(type="string", length=255) + */ + public $name; + + /** + * @Version @Column(type="datetime") + */ + public $version; } \ No newline at end of file