A database transaction is a unit of interaction with a database management system or similar system that is treated in a coherent and reliable way independent of other transactions that must be either entirely completed or aborted. Ideally, a database system will guarantee all of the ACID (Atomicity, Consistency, Isolation, and Durability) properties for each transaction.
In Doctrine all operations are wrapped in transactions by default. There are some things that should be noticed about how Doctrine works internally:
First we need to begin a new transation:
$conn->beginTransaction();
Next perform some operations which result in queries being executed:
$user = new User();
$user->name = 'New user';
$user->save();
$user = Doctrine_Core::getTable('User')->find(5);
$user->name = 'Modified user';
$user->save();
Now we can commit all the queries by using the commit() method:
$conn->commit();
You can easily nest transactions with the Doctrine DBAL. Check the code below for a simple example demonstrating nested transactions.
First lets create a standard PHP function named saveUserAndGroup():
function saveUserAndGroup(Doctrine_Connection $conn, User $user, Group $group)
{
$conn->beginTransaction();
$user->save();
$group->save();
$conn->commit();
}
Now we make use of the function inside another transaction:
try {
$conn->beginTransaction();
saveUserAndGroup($conn,$user,$group);
saveUserAndGroup($conn,$user2,$group2);
saveUserAndGroup($conn,$user3,$group3);
$conn->commit();
} catch(Doctrine_Exception $e) {
$conn->rollback();
}
Notice how the three calls to saveUserAndGroup() are wrapped in a transaction, and each call to the function starts its own nested transaction.
Doctrine supports transaction savepoints. This means you can set named transactions and have them nested.
The Doctrine_Transaction::beginTransaction($savepoint) sets a named transaction savepoint with a name of $savepoint. If the current transaction has a savepoint with the same name, the old savepoint is deleted and a new one is set.
try {
$conn->beginTransaction();
// do some operations here
// creates a new savepoint called mysavepoint
$conn->beginTransaction('mysavepoint');
try {
// do some operations here
$conn->commit('mysavepoint');
} catch(Exception $e) {
$conn->rollback('mysavepoint');
}
$conn->commit();
} catch(Exception $e) {
$conn->rollback();
}
The Doctrine_Transaction::rollback($savepoint) rolls back a transaction to the named savepoint. Modifications that the current transaction made to rows after the savepoint was set are undone in the rollback.
Mysql, for example, does not release the row locks that were stored in memory after the savepoint.
Savepoints that were set at a later time than the named savepoint are deleted.
The Doctrine_Transaction::commit($savepoint) removes the named savepoint from the set of savepoints of the current transaction.
All savepoints of the current transaction are deleted if you execute a commit or if a rollback is being called without savepoint name parameter.
try {
$conn->beginTransaction();
// do some operations here
// creates a new savepoint called mysavepoint
$conn->beginTransaction('mysavepoint');
// do some operations here
$conn->commit(); // deletes all savepoints
} catch(Exception $e) {
$conn->rollback(); // deletes all savepoints
}
A transaction isolation level sets the default transactional behavior. As the name 'isolation level' suggests, the setting determines how isolated each transation is, or what kind of locks are associated with queries inside a transaction. The four available levels are (in ascending order of strictness):
To get the transaction module use the following code:
$tx = $conn->transaction;
Set the isolation level to READ COMMITTED:
$tx->setIsolation('READ COMMITTED');
Set the isolation level to SERIALIZABLE:
$tx->setIsolation('SERIALIZABLE');
Some drivers (like Mysql) support the fetching of current transaction isolation level. It can be done as follows:
$level = $tx->getIsolation();
Transactions are a great feature for ensuring the quality and consistency of your database. Now that you know about transactions we are ready to move on and learn about the events sub-framework.
The events sub-framework is a great feature that allows you to hook in to core methods of Doctrine and alter the operations of internal functionality without modifying one line of core code.