A Transaction forms a unit of work. The Transaction manages what happens within that unit
of work, and when an error occurs the Transaction can roll back any changes performed.
Transactions can be managed by the users application, or can be managed by a framework
(such as Spring), or can be managed by a J2EE container. These are described below.
See also :-
When using a JDO implementation such as DataNucleus in a J2SE environment, the transactions
are by default
Locally Managed Transactions
. The users code will manage the
transactions by starting, and commiting the transaction itself. With these transactions
with JDO
you would do something like
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
{users code to persist objects}
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
pm.close();
When you use a framework like Spring
you would not need to specify the
tx.begin(), tx.commit(), tx.rollback()
since that would
be done for you. The basic idea with
Locally Managed transactions
is that you are
managing the transaction start and end.
When using a JDO implementation such as DataNucleus in a J2SE environment, you can also
make use of
JTA Transactions
.
You define the persistence property
javax.jdo.option.TransactionType
setting it to "JTA"
.
Then you make use of JTA (or JDO) to demarcate the transactions.
So you could do something like
UserTransaction ut = (UserTransaction)
new InitialContext().lookup("java:comp/UserTransaction");
PersistenceManager pm = pmf.getPersistenceManager();
try
{
ut.begin();
{users code to persist objects}
ut.commit();
}
finally
{
pm.close();
}
So here we used the JTA API to begin/commit the controlling
(
javax.transaction.UserTransaction
).
An alternative is where you don't have a UserTransaction started and just use the
JDO API, which will start the UserTransaction for you.
UserTransaction ut = (UserTransaction)
new InitialContext().lookup("java:comp/UserTransaction");
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin(); // Starts the UserTransaction
{users code to persist objects}
tx.commit(); // Commits the UserTransaction
}
finally
{
pm.close();
}
Important
: please note that you need to set both transactional and
nontransactional datasources, and
the nontransactional cannot be JTA
.
When using a J2EE container you are giving over control of the transactions to the container.
Here you have
Container Managed Transactions
. In terms of your code, you would do like
the previous example
except
that you would OMIT the
tx.begin(), tx.commit(),
tx.rollback()
since the J2EE container will be doing this for you.
JDO 2.2 provides a mechanism for specification of the transaction isolation level.
This can be specified globally via the PersistenceManagerFactory property
datanucleus.transactionIsolation
(javax.jdo.option.TransactionIsolationLevel).
It accepts the following values
-
read-uncommitted
: dirty reads, non-repeatable reads and phantom reads can occur
-
read-committed
: dirty reads are prevented; non-repeatable reads and phantom reads can occur
-
repeatable-read
: dirty reads and non-repeatable reads are prevented; phantom reads can occur
-
serializable
: dirty reads, non-repeatable reads and phantom reads are prevented
The default (in DataNucleus) is
read-committed
. An attempt to set the isolation level
to an unsupported value (for the datastore) will throw a JDOUserException.
For example DB4O only supports
read-committed
so will always use that.
As an alternative you can also specify it on a per-transaction basis as follows (using the
names above).
Transaction tx = pm.currentTransaction();
...
tx.setIsolationLevel("read-committed");
There are situations where you may want to get notified that a transaction is in course of
being committed or rolling back. To make that happen, you would do something like
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
try
{
tx.begin();
tx.setSynchronization(new javax.transaction.Synchronization()
{
public void beforeCompletion()
{
// before commit or rollback
}
public void afterCompletion(int status)
{
if (status == javax.transaction.Status.STATUS_ROLLEDBACK)
{
// rollback
}
else if (status == javax.transaction.Status.STATUS_COMMITTED)
{
// commit
}
}
});
tx.commit();
}
finally
{
if (tx.isActive())
{
tx.rollback();
}
}
pm.close();