NAME
DBIx::Transaction::db - Database handle that is aware of nested transactions
SYNOPSIS
DESCRIPTION
When you connect to a database using DBIx::Transaction, your database handle will be a DBIx::Transaction::db object. These objects keep track of your transaction state, allowing for transactions to occur within transactions, and only sending "commit
" or "rollback
" instructions to the underlying database when the outermost transaction has completed. See DBIx::Transaction for a more complete explanation.
METHODS
Overridden Methods
The following methods are overridden by DBIx::Transaction::db:
- begin_work
-
Start a transaction.
If there are no transactions currently running,
begin_work
will check ifAutoCommit
is enabled. If it is enabled, abegin_work
instruction is sent to the underlying database layer. IfAutoCommit
is disabled, we assume that the database has already started a transaction for us, and do nothing. This means that you must always use begin_work to start a transaction, even ifAutoCommit
is enabled!If there is a transaction started,
begin_work
simply records that a nested transaction has started.begin_work
returns the result of the database'sbegin_work
call if it makes one; otherwise it always returns true. - rollback
-
Abort a transaction.
If there are no sub-transactions currently running,
rollback
will issue therollback
call to the underlying database layer.If there are sub-transactions currently running,
rollback
notes that the nested transaction has been aborted.If there is no transaction running at all,
rollback
will raise a fatal error. - commit
-
If there are sub-transactions currently running,
commit
records that this transaction has completed successfully and does nothing to the underlying database layer.If there are no sub-transactions currently running,
commit
checks if there have been any transaction errors. If there has been a transaction error,commit
raises an exception. Otherwise, acommit
call is issued to the underlying database layer.If there is no transaction running at all,
commit
will raise a fatal error. This error will contain a full stack trace, and should also contain the file names and line numbers where any rollbacks or query failures happened. - do
-
Calls do() on your underlying database handle. If an error occurs, this is recorded and you will not be able to issue a
commit
for the current transaction.
Extra Methods
The following method is provided for convienence in setting up database transactions:
- transaction($coderef)
-
Execute the code contained inside
$coderef
within a transaction.$coderef
is expected to return a scalar value. If$coderef
returns true, the transaction is committed. If$coderef
returns false or raises a fatal error, the transaction is rolled back. The return value is the same as what is returned by$coderef
.This method is supplied to make it easier to create nested transactions out of many small transactions. Example:
sub get_max_id { my $dbh = shift; # this will roll back if it can't get MAX(num) $dbh->transaction(sub { if(my($id) = $dbh->selectrow_array("SELECT MAX(num) FROM foo")) { return $id; } else { return; } }); } sub do_something { my($dbh, $num) = @_; $dbh->transaction(sub { $dbh->do("UPDATE foo SET bar = bar + 1 WHERE num = $num"); }); } sub do_many_things { my $dbh = shift; # if any of these sub-transactions roll back, the whole thing will roll # back $dbh->transaction(sub { if( do_something($dbh, 1) && do_something($dbh, 2) && (my $id = get_max_id($dbh)) ) { return do_something($dbh, $id); } else { return; } }); }
Other Methods
The following methods are used by the overridden methods. In most cases you won't have to use them yourself.
- transaction_level
-
Returns an integer value representing how deeply nested our transactions currently are. eg; if we are in a top-level transaction, this returns "1"; if we are 4 transactions deep, this returns "4", if we are not in a transaction at all, this returns "0". In some extreme cases this may be used to bail out of a nested transaction safely, as in:
$dbh->rollback while $dbh->transaction_level;
But I would suggest that you structure your code so that each transaction and sub-transaction bails out safely instead, as that's a lot easier to trace and debug with confidence.
- transaction_error
-
Returns a true value if a sub-transaction has rolled back, false otherwise. Again, you could use this to back out of a transaction safely, but I'd suggest just writing your code to handle this case on each transaction level instead.
- transaction_trace
-
For debugging; If DBI's trace level is 3 or over, emit the current values of all of the internal variables DBIx::Transaction uses to track it's transaction states.
- inc_transaction_level
-
Indicate that we have started a sub transaction by increasing
transaction_level
by one. This is used by thebegin_work
override and should not be called directly. - dec_transaction_level
-
Indicate that we have finished a sub transaction by decrementing
transaction_level
by one. If this results in a negative number (meaning more transactions have been commited/rolled back than started),dec_transaction_level
throws a fatal error. This is used by thecommit
androllback
methods and should not be called directly. - inc_transaction_error
-
Indicate that a sub-transaction has failed and that the entire transaction should not be allowed to be committed. This is done automatically whenever a sub-transaction issues a
rollback
. Optional parameters are the package, filename, and line where the transaction error occured. If provided, they will be used in error messages relating to the rollback. - clear_transaction_error
-
Clear the transaction error flag. This flag is set whenever a sub-transaction issues a
rollback
, and cleared whenever the outermost transaction issues arollback
. - close_transaction($method)
-
Close the outermost transaction by calling
$method
("commit
" or "rollback
") on the underlying database layer and resetting the DBIx::Transaction state. This method is used by thecommit
androllback
methods and you shouldn't need to use it yourself, unless you wanted to forcibly bail out of an entire transaction without callingrollback
repeatedly, but as stated above, that's a bad idea, isn't it?
SEE ALSO
AUTHOR
Tyler "Crackerjack" MacDonald <japh@crackerjack.net>
LICENSE
Copyright 2005 Tyler MacDonald This is free software; you may redistribute it under the same terms as perl itself.