public abstract class Transaction<T> extends Object
Allows for the syntactically simple use of database transactions within the
ActiveObjects API. This class's syntax is modeled after the transaction
do ... end
syntax provided by Rails's ActiveRecord ORM. The intention
is to provide the simplest possible encapsulation around the transaction
functionality. As such, AO transactions lack some of the power of the
underlying database transaction function (such as arbitrary save-points).
The design behind Transaction
is modeled after the
following code snippet:
new Transaction<Object>(manager) { public Object run() { Account a = getEntityManager().get(Account.class, 1); Account b = getEntityManager().get(Account.class, 2); a.setBalance(a.getBalance() - 1000); a.save(); b.setBalance(b.getBalance() + 1000); b.save(); return null; } }.execute();
The transaction will be committed only after the run()
method returns. Thus, a.save()
doesn't immediately modify
the database values, only upon the committal of the transaction. If any
conflicts are detected, JDBC will automatically throw an SQLException
.
Transaction
catches this exception and rolls back the
transaction, ensuring data integrity. Once the transaction is rolled back, the
exception is rethrown from the execute()
method.
In cases where the transaction generates data which must be returned, this
can be accomplished by returning from the run()
method against the
parameterized type. Thus if a transaction to create an account is utilized:
Account result = new Transaction<Account>(manager) { public Account run() throws SQLException { Account back = getEntityManager().create(Account.class); back.setBalance(0); back.save(): return back; } }.execute();
The value returned from run()
will be passed back up the call
stack to execute()
, which will return the value to the caller.
Thus in this example, result
will be precisely the back
instance from within the transaction. This feature allows data to escape the
scope of the transaction, thereby achieving a greater usefulness.
The JDBC transaction type used is Connection.TRANSACTION_SERIALIZABLE
.
Connection
Constructor and Description |
---|
Transaction(EntityManager manager)
Creates a new
Transaction using the specified
EntityManager instance. |
Modifier and Type | Method and Description |
---|---|
T |
execute()
Executes the transaction defined within the overridden
run()
method. |
protected EntityManager |
getEntityManager() |
protected abstract T |
run()
Called internally by
execute() to actually perform the actions
within the transaction. |
public Transaction(EntityManager manager)
Transaction
using the specified
EntityManager
instance. If the specified instance is null
,
an exception will be thrown.manager
- The EntityManager
instance against which the
transaction should run.IllegalArgumentException
- If the EntityManager
instance is null
.protected final EntityManager getEntityManager()
public T execute() throws SQLException
Executes the transaction defined within the overridden run()
method. If the transaction fails for any reason (such as a conflict), it will
be rolled back and an exception thrown. The value returned from the
run()
method will be returned from execute()
.
Custom JDBC code can be executed within a transaction. However, one
should be a bit careful with the mutable state of the Connection
instance obtained from getEntityManager().getProvider().getConnection()
.
This is because it is this exact instance which is used in all database
operations for that transaction. Thus it is technically possible to commit a
transaction prematurely, disable the transaction entirely, or otherwise really
mess up the internals of the implementation. You do not have to
call setAutoCommit(boolean)
on the Connection
instance retrieved from the DatabaseProvider
. The connection is
already initialized and within an open transaction by the time it gets to your
custom code within the transaction.
run()
SQLException
- If the transaction failed for any reason and was rolled back.run()
protected abstract T run() throws SQLException
Called internally by execute()
to actually perform the actions
within the transaction. Any SQLException(s)
should be
allowed to propogate back up to the calling method, which will ensure
that the transaction is rolled back and the proper resources disposed. If
the transaction generates a value which must be passed back to the calling
method, this value may be returned as long as it is of the parameterized
type. If no value is generated, null
is an acceptable return
value.
Be aware that any operations performed within a transaction
(even if indirectly invoked by the run()
method) will use
the exact same Connection
instance. This is to ensure
integrity of the transaction's operations while at the same time allowing
custom JDBC code and queries within the transaction.
null
.SQLException
- If something has gone wrong within the transaction and
it requires a roll-back.Copyright © 2007–2021 Atlassian. All rights reserved.