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

Added control abstractions for transaction demarcation.

parent e62b51cf
......@@ -19,7 +19,7 @@
namespace Doctrine\DBAL;
use PDO, Closure,
use PDO, Closure, Exception,
Doctrine\DBAL\Types\Type,
Doctrine\DBAL\Driver\Connection as DriverConnection,
Doctrine\Common\EventManager,
......@@ -705,6 +705,28 @@ class Connection implements DriverConnection
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.
*
......
......@@ -19,7 +19,8 @@
namespace Doctrine\ORM;
use Doctrine\Common\EventManager,
use Closure, Exception,
Doctrine\Common\EventManager,
Doctrine\DBAL\Connection,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\ORM\Mapping\ClassMetadataFactory,
......@@ -176,6 +177,32 @@ class EntityManager
$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.
*
......
......@@ -1290,6 +1290,8 @@ class UnitOfWork implements PropertyChangedListener
* @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.
*
* @todo Require active transaction!? OptimisticLockException may result in undefined state!?
*/
public function merge($entity)
{
......
......@@ -59,6 +59,16 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->_conn->rollback();
$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
$address->zip = '12345';
$user->setAddress($address);
$this->_em->persist($user);
$this->_em->flush();
$this->_em->transactional(function($em) use($user) {
$em->persist($user);
});
$this->_em->clear();
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
......@@ -661,7 +662,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Germany', $address2->country);
$this->assertEquals('Berlin', $address2->city);
$this->assertEquals('12345', $address2->zip);
}
//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