Commit f619a15a authored by Roman S. Borschel's avatar Roman S. Borschel

Removed EntityTransaction until it has a real purpose. Added the affected...

Removed EntityTransaction until it has a real purpose. Added the affected entity to OptimisticLockException. Updated functional optimistic locking tests accordingly.
parent d0325d70
<?php
/*
* 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\DBAL;
......@@ -51,5 +68,5 @@ interface Driver
* @param Doctrine\DBAL\Connection $conn
* @return string $database
*/
public function getDatabase(\Doctrine\DBAL\Connection $conn);
public function getDatabase(Connection $conn);
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ interface Connection
function quote($input, $type=\PDO::PARAM_STR);
function exec($statement);
function lastInsertId($name = null);
function beginTransaction();
function beginTransaction();
function commit();
function rollBack();
function errorCode();
......
<?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
......@@ -25,13 +23,10 @@ use Doctrine\DBAL\Driver,
Doctrine\DBAL\Connection;
/**
* IBM Db2 Driver
* IBM DB2 Driver
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class DB2Driver implements Driver
{
......
<?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
......
<?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
......
<?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
......
......@@ -102,13 +102,6 @@ class EntityManager
*/
private $_closed = false;
/**
* The ORM Entity Transaction.
*
* @var Doctrine\ORM\EntityTransaction
*/
protected $_transaction;
/**
* Creates a new EntityManager that operates on the given database connection
* and uses the given Configuration and EventManager implementations.
......@@ -129,7 +122,6 @@ class EntityManager
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses());
$this->_transaction = new EntityTransaction($this);
}
/**
......@@ -152,26 +144,17 @@ class EntityManager
return $this->_metadataFactory;
}
/**
* Gets the ORM Transaction instance.
*
* @return Doctrine\ORM\EntityTransaction
*/
public function getTransaction()
{
return $this->_transaction;
}
/**
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
*
* Example:
*
* [php]
* <code>
* $qb = $em->createQueryBuilder();
* $expr = $em->getExpressionBuilder();
* $qb->select('u')->from('User', 'u')
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
* </code>
*
* @return ExpressionBuilder
*/
......@@ -185,29 +168,32 @@ class EntityManager
/**
* Starts a transaction on the underlying database connection.
*
* @deprecated Use {@link getConnection}.beginTransaction().
*/
public function beginTransaction()
{
$this->getTransaction()->begin();
$this->_conn->beginTransaction();
}
/**
* Commits a transaction on the underlying database connection.
*
* @deprecated Use {@link getConnection}.commit().
*/
public function commit()
{
$this->getTransaction()->commit();
$this->_conn->commit();
}
/**
* Performs a rollback on the underlying database connection and closes the
* EntityManager as it may now be in a corrupted state.
* Performs a rollback on the underlying database connection.
*
* @return boolean TRUE on success, FALSE on failure
* @deprecated Use {@link getConnection}.rollback().
*/
public function rollback()
{
return $this->getTransaction()->rollback();
$this->_conn->rollback();
}
/**
......@@ -288,6 +274,9 @@ class EntityManager
* Flushes all changes to objects that have been queued up to now to the database.
* This effectively synchronizes the in-memory state of managed objects with the
* database.
*
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
* makes use of optimistic locking fails.
*/
public function flush()
{
......
<?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;
use Doctrine\DBAL\Transaction;
/**
* The Transaction class is the central access point to ORM Transaction functionality.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
final class EntityTransaction
{
/**
* The wrapped ORM EntityManager.
*
* @var Doctrine\ORM\EntityManager
*/
private $_em;
/**
* The database connection used by the EntityManager.
*
* @var Doctrine\DBAL\Connection
*/
private $_conn;
/**
* Constructor.
*
* @param Transaction $transaction
*/
public function __construct(EntityManager $em)
{
$this->_em = $em;
$this->_conn = $em->getConnection();
}
/**
* Checks whether a transaction is currently active.
*
* @return boolean TRUE if a transaction is currently active, FALSE otherwise.
*/
public function isTransactionActive()
{
return $this->_conn->isTransactionActive();
}
/**
* Sets the transaction isolation level.
*
* @param integer $level The level to set.
*/
public function setTransactionIsolation($level)
{
return $this->_conn->setTransactionIsolation($level);
}
/**
* Gets the currently active transaction isolation level.
*
* @return integer The current transaction isolation level.
*/
public function getTransactionIsolation()
{
return $this->_conn->getTransactionIsolation();
}
/**
* Returns the current transaction nesting level.
*
* @return integer The nesting level. A value of 0 means there's no active transaction.
*/
public function getTransactionNestingLevel()
{
return $this->_conn->getTransactionNestingLevel();
}
/**
* Starts a transaction by suspending auto-commit mode.
*
* @return void
*/
public function begin()
{
$this->_conn->beginTransaction();
}
/**
* Commits the current transaction.
*
* @return void
* @throws Doctrine\DBAL\ConnectionException If the commit failed due to no active transaction or
* because the transaction was marked for rollback only.
*/
public function commit()
{
$this->_conn->commit();
}
/**
* Cancel any database changes done during the current transaction.
*
* this method can be listened with onPreTransactionRollback and onTransactionRollback
* event listener methods
*
* @return boolean TRUE on success, FALSE on failure
* @throws Doctrine\DBAL\ConnectionException If the rollback operation failed.
*/
public function rollback()
{
$this->_em->close();
return $this->_conn->rollback();
}
/**
* Marks the current transaction so that the only possible
* outcome for the transaction to be rolled back.
*
* @throws Doctrine\DBAL\ConnectionException If no transaction is active.
*/
public function setRollbackOnly()
{
$this->_conn->setRollbackOnly();
}
/**
* Check whether the current transaction is marked for rollback only.
*
* @return boolean
* @throws Doctrine\DBAL\ConnectionException If no transaction is active.
*/
public function isRollbackOnly()
{
return $this->_conn->isRollbackOnly();
}
}
\ No newline at end of file
......@@ -19,13 +19,15 @@
namespace Doctrine\ORM;
use Exception;
/**
* Base exception class for all ORM exceptions.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class ORMException extends \Exception
class ORMException extends Exception
{
public static function missingMappingDriverImpl()
{
......
......@@ -20,15 +20,33 @@
namespace Doctrine\ORM;
/**
* OptimisticLockException
* An OptimisticLockException is thrown when a version check on an object
* that uses optimistic locking through a version field fails.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class OptimisticLockException extends ORMException
{
public static function lockFailed()
private $entity;
public function __construct($msg, $entity)
{
$this->entity = $entity;
}
/**
* Gets the entity that caused the exception.
*
* @return object
*/
public function getEntity()
{
return $this->entity;
}
public static function lockFailed($entity)
{
return new self("The optimistic lock failed.");
return new self("The optimistic lock on an entity failed.", $entity);
}
}
\ No newline at end of file
......@@ -330,7 +330,7 @@ class BasicEntityPersister
$result = $this->_conn->executeUpdate($sql, $params, $types);
if ($this->_class->isVersioned && ! $result) {
throw OptimisticLockException::lockFailed();
throw OptimisticLockException::lockFailed($entity);
}
}
......
......@@ -19,7 +19,8 @@
namespace Doctrine\ORM;
use Doctrine\Common\Collections\ArrayCollection,
use Exception,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\Collection,
Doctrine\Common\NotifyPropertyChanged,
Doctrine\Common\PropertyChangedListener,
......@@ -276,18 +277,17 @@ class UnitOfWork implements PropertyChangedListener
// Now we need a commit order to maintain referential integrity
$commitOrder = $this->_getCommitOrder();
$tx = $this->_em->getTransaction();
try {
$tx->begin();
$conn = $this->_em->getConnection();
$conn->beginTransaction();
try {
if ($this->_entityInsertions) {
foreach ($commitOrder as $class) {
$this->_executeInserts($class);
}
}
if ($this->_entityUpdates) {
foreach ($commitOrder as $class) {
$this->_executeUpdates($class);
......@@ -317,11 +317,10 @@ class UnitOfWork implements PropertyChangedListener
}
}
$tx->commit();
} catch (\Exception $e) {
$tx->setRollbackOnly();
$tx->rollback();
$conn->commit();
} catch (Exception $e) {
$this->_em->close();
$conn->rollback();
throw $e;
}
......@@ -1289,6 +1288,8 @@ class UnitOfWork implements PropertyChangedListener
*
* @param object $entity
* @return object The managed copy of the entity.
* @throws OptimisticLockException If the entity uses optimistic locking through a version
* attribute and the version check against the managed copy fails.
*/
public function merge($entity)
{
......@@ -1315,7 +1316,7 @@ class UnitOfWork implements PropertyChangedListener
throw new \InvalidArgumentException('New entity detected during merge.'
. ' Persist the new entity before merging.');
}
// MANAGED entities are ignored by the merge operation
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
$managedCopy = $entity;
......@@ -1343,7 +1344,7 @@ class UnitOfWork implements PropertyChangedListener
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailed();
throw OptimisticLockException::lockFailed($entity);
}
}
......
......@@ -25,7 +25,7 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
//no rethrow
}
$this->assertTrue($this->_conn->getRollbackOnly());
$this->assertTrue($this->_conn->isRollbackOnly());
$this->_conn->commit(); // should throw exception
$this->fail('Transaction commit after failed nested transaction should fail.');
......
......@@ -84,6 +84,44 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$phonenumbers = $user->getPhonenumbers();
$this->assertTrue($this->_em->contains($phonenumbers[0]));
$this->assertTrue($this->_em->contains($phonenumbers[1]));
}
}
/**
* @group DDC-518
*/
/*public function testMergeDetachedEntityWithNewlyPersistentOneToOneAssoc()
{
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
// Create a detached user
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'dev';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
//$address = new CmsAddress;
//$address->city = 'Berlin';
//$address->country = 'Germany';
//$address->street = 'Sesamestreet';
//$address->zip = 12345;
//$address->setUser($user);
$phone = new CmsPhonenumber();
$phone->phonenumber = '12345';
$user2 = $this->_em->merge($user);
$user2->addPhonenumber($phone);
$this->_em->persist($phone);
//$address->setUser($user2);
//$this->_em->persist($address);
$this->_em->flush();
$this->assertEquals(1,1);
}*/
}
......@@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Functional\Locking;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\Common\EventManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\Tests\TestUtil;
......@@ -37,15 +38,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$this->assertEquals(1, $test->version);
return $test;
}
/**
* @expectedException Doctrine\ORM\OptimisticLockException
* @depends testJoinedChildInsertSetsInitialVersionValue
*/
public function testJoinedChildFailureThrowsException()
public function testJoinedChildFailureThrowsException(OptimisticJoinedChild $child)
{
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild t WHERE t.name = :name');
$q->setParameter('name', 'child');
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild t WHERE t.id = :id');
$q->setParameter('id', $child->id);
$test = $q->getSingleResult();
// Manually update/increment the version so we can try and save the same
......@@ -55,7 +58,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
// Now lets change a property and try and save it again
$test->whatever = 'ok';
$this->_em->flush();
try {
$this->_em->flush();
} catch (OptimisticLockException $e) {
$this->assertSame($test, $e->getEntity());
}
}
public function testJoinedParentInsertSetsInitialVersionValue()
......@@ -66,15 +73,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$this->assertEquals(1, $test->version);
return $test;
}
/**
* @expectedException Doctrine\ORM\OptimisticLockException
* @depends testJoinedParentInsertSetsInitialVersionValue
*/
public function testJoinedParentFailureThrowsException()
public function testJoinedParentFailureThrowsException(OptimisticJoinedParent $parent)
{
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent t WHERE t.name = :name');
$q->setParameter('name', 'parent');
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent t WHERE t.id = :id');
$q->setParameter('id', $parent->id);
$test = $q->getSingleResult();
// Manually update/increment the version so we can try and save the same
......@@ -84,7 +93,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
// Now lets change a property and try and save it again
$test->name = 'WHATT???';
$this->_em->flush();
try {
$this->_em->flush();
} catch (OptimisticLockException $e) {
$this->assertSame($test, $e->getEntity());
}
}
public function testStandardInsertSetsInitialVersionValue()
......@@ -95,15 +108,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$this->assertEquals(1, $test->getVersion());
return $test;
}
/**
* @expectedException Doctrine\ORM\OptimisticLockException
* @depends testStandardInsertSetsInitialVersionValue
*/
public function testStandardFailureThrowsException()
public function testStandardFailureThrowsException(OptimisticStandard $entity)
{
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard t WHERE t.name = :name');
$q->setParameter('name', 'test');
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard t WHERE t.id = :id');
$q->setParameter('id', $entity->id);
$test = $q->getSingleResult();
// Manually update/increment the version so we can try and save the same
......@@ -113,7 +128,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
// Now lets change a property and try and save it again
$test->name = 'WHATT???';
$this->_em->flush();
try {
$this->_em->flush();
} catch (OptimisticLockException $e) {
$this->assertSame($test, $e->getEntity());
}
}
public function testOptimisticTimestampSetsDefaultValue()
......@@ -124,15 +143,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$this->assertTrue(strtotime($test->version) > 0);
return $test;
}
/**
* @expectedException Doctrine\ORM\OptimisticLockException
* @depends testOptimisticTimestampSetsDefaultValue
*/
public function testOptimisticTimestampFailureThrowsException()
public function testOptimisticTimestampFailureThrowsException(OptimisticTimestamp $entity)
{
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.name = :name');
$q->setParameter('name', 'Testing');
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
$q->setParameter('id', $entity->id);
$test = $q->getSingleResult();
$this->assertType('DateTime', $test->version);
......@@ -143,7 +164,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
// Try and update the record and it should throw an exception
$test->name = 'Testing again';
$this->_em->flush();
try {
$this->_em->flush();
} catch (OptimisticLockException $e) {
$this->assertSame($test, $e->getEntity());
}
}
}
......
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