Commit 5dbd4056 authored by Roman S. Borschel's avatar Roman S. Borschel

Added control abstractions for transaction demarcation.

parent e62b51cf
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
namespace Doctrine\DBAL; namespace Doctrine\DBAL;
use PDO, Closure, use PDO, Closure, Exception,
Doctrine\DBAL\Types\Type, Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Driver\Connection as DriverConnection, Doctrine\DBAL\Driver\Connection as DriverConnection,
Doctrine\Common\EventManager, Doctrine\Common\EventManager,
...@@ -705,6 +705,28 @@ class Connection implements DriverConnection ...@@ -705,6 +705,28 @@ class Connection implements DriverConnection
return $this->_conn->lastInsertId($seqName); return $this->_conn->lastInsertId($seqName);
} }
/**
* Executes a function in a transaction.
*
* The function gets passed this Connection instance as an (optional) parameter.
*
* If an exception occurs during execution of the function or transaction commit,
* the transaction is rolled back and the exception re-thrown.
*
* @param Closure $func The function to execute transactionally.
*/
public function transactional(Closure $func)
{
$this->beginTransaction();
try {
$func($this);
$this->commit();
} catch (Exception $e) {
$this->rollback();
throw $e;
}
}
/** /**
* Starts a transaction by suspending auto-commit mode. * Starts a transaction by suspending auto-commit mode.
* *
......
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\Common\EventManager, use Closure, Exception,
Doctrine\Common\EventManager,
Doctrine\DBAL\Connection, Doctrine\DBAL\Connection,
Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataFactory, Doctrine\ORM\Mapping\ClassMetadataFactory,
...@@ -176,6 +177,32 @@ class EntityManager ...@@ -176,6 +177,32 @@ class EntityManager
$this->_conn->beginTransaction(); $this->_conn->beginTransaction();
} }
/**
* Executes a function in a transaction.
*
* The function gets passed this EntityManager instance as an (optional) parameter.
*
* {@link flush} is invoked prior to transaction commit.
*
* If an exception occurs during execution of the function or flushing or transaction commit,
* the transaction is rolled back, the EntityManager closed and the exception re-thrown.
*
* @param Closure $func The function to execute transactionally.
*/
public function transactional(Closure $func)
{
$this->_conn->beginTransaction();
try {
$func($this);
$this->flush();
$this->_conn->commit();
} catch (Exception $e) {
$this->close();
$this->_conn->rollback();
throw $e;
}
}
/** /**
* Commits a transaction on the underlying database connection. * Commits a transaction on the underlying database connection.
* *
......
...@@ -1290,6 +1290,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1290,6 +1290,8 @@ class UnitOfWork implements PropertyChangedListener
* @return object The managed copy of the entity. * @return object The managed copy of the entity.
* @throws OptimisticLockException If the entity uses optimistic locking through a version * @throws OptimisticLockException If the entity uses optimistic locking through a version
* attribute and the version check against the managed copy fails. * attribute and the version check against the managed copy fails.
*
* @todo Require active transaction!? OptimisticLockException may result in undefined state!?
*/ */
public function merge($entity) public function merge($entity)
{ {
......
...@@ -59,6 +59,16 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -59,6 +59,16 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->_conn->rollback(); $this->_conn->rollback();
$this->assertEquals(0, $this->_conn->getTransactionNestingLevel()); $this->assertEquals(0, $this->_conn->getTransactionNestingLevel());
} }
$this->assertEquals(0, $this->_conn->getTransactionNestingLevel());
try {
$this->_conn->transactional(function($conn) {
$conn->executeQuery("select 1");
throw new \RuntimeException("Ooops!");
});
} catch (\RuntimeException $expected) {
$this->assertEquals(0, $this->_conn->getTransactionNestingLevel());
}
} }
} }
\ No newline at end of file
...@@ -643,9 +643,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -643,9 +643,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$address->zip = '12345'; $address->zip = '12345';
$user->setAddress($address); $user->setAddress($address);
$this->_em->persist($user); $this->_em->transactional(function($em) use($user) {
$this->_em->flush(); $em->persist($user);
});
$this->_em->clear(); $this->_em->clear();
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
...@@ -661,7 +662,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -661,7 +662,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Germany', $address2->country); $this->assertEquals('Germany', $address2->country);
$this->assertEquals('Berlin', $address2->city); $this->assertEquals('Berlin', $address2->city);
$this->assertEquals('12345', $address2->zip); $this->assertEquals('12345', $address2->zip);
} }
//DRAFT OF EXPECTED/DESIRED BEHAVIOR //DRAFT OF EXPECTED/DESIRED BEHAVIOR
......
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