Java Persistence/Transactions

=Transactions=

A transaction is a set of operations that either fail or succeed as a unit. Transactions are a fundamental part of persistence. A database transaction consists of a set of SQL DML (Data Manipulation Language) operations that are committed or rolled back as a single unit. An object level transaction is one in which a set of changes made to a set of objects are committed to the database as a single unit.

JPA provides two mechanisms for transactions. When used in Java EE JPA provides integration with JTA (Java Transaction API). JPA also provides its own  implementation to Java SE and for use in a non-managed mode in Java EE. Transactions in JPA are always at the object level, this means that all changes made to all persistent objects in the persistence context are part of the transaction.

Resource Local Transactions
Resource local transactions are used in JSE, or in application managed (non-managed) mode in Java EE. To use resource local transactions the  attribute in the   is set to. If resource local transactions are used with a, the   element should be used to reference a server   that has been configured to not be JTA managed.

Local JPA transactions are defined through the class. It contains basic transaction API including,   and.

Technically in JPA the  is in a transaction from the point it is created. So  is somewhat redundant. Until  is called, certain operations such as ,  ,   cannot be called. Queries can still be performed, and objects that were queried can be changed, although this is somewhat unspecified what will happen to these changes in the JPA spec, normally they will be committed, however it is best to call  before making any changes to your objects. Normally it is best to create a new  for each transaction to avoid have stale objects remaining in the persistence context, and to allow previously managed objects to garbage collect.

After a successful  the   can continue to be used, and all of the managed objects remain managed. However it is normally best to  or   the   to allow garbage collection and avoid stale data. If the commit fails, then the managed objects are considered detached, and the  is cleared. This means that commit failures cannot be caught and retried, if a failure occurs, the entire transaction must be performed again. The previously managed object may also be left in an inconsistent state, meaning some of the objects locking version may have been incremented. Commit will also fail if the transaction has been marked for rollback. This can occur either explicitly by calling  or is required to be set if any query or find operation fails. This can be an issue, as some queries may fail, but may not be desired to cause the entire transaction to be rolled back.

The  operation will rollback the database transaction only. The managed objects in the persistence context will become detached and the  is cleared. This means any object previously read, should no longer be used, and is no longer part of the persistence context. The changes made to the objects will be left as is, the object changes will not be reverted.

JTA Transactions
JTA transactions are used in Java EE, in managed mode (EJB). To use JTA transactions the  attribute in the   is set to. If JTA transactions are used with a, the   element should be used to reference a server   that has been configure to be JTA managed.

JTA transactions are defined through the JTA class, or more likely implicitly defined through   usage/methods. In a  each   method defaults to a     and hence its invocation starts a JTA transaction, if no JTA transaction is in progress. If a method is the first called that requires a transaction, it will commit the changes upon completion.

JTA transaction can be shared among  methods, hence if a method is called through it's business interface from another method that already started a transaction, the second method will work inside the existing transaction. Moreover the second method can only change the state of the  by persisting objects or doing other operations, and when it returns control to the calling method it delegates to it the execution of the actual. To perform DB operations in a transaction isolated from any transaction in progress a  method can be annotated as. The s that can be associate to   methods are:

can be obtained through a JNDI lookup in most application servers, or from the  in EJB 2.0 style  s.

JTA transactions can be used in two modes in Java EE. In Java EE managed mode, such as an  injected into a , the   reference, represents a new persistence context for each transaction. This means objects read in one transaction become detached after the end of the transaction, and should no longer be used, or need to be merged into the next transaction. In managed mode, you never create or close an.

The second mode allows the  to be application managed, (normally obtained from an injected , or directly from JPA  ). This allows the persistence context to survive transaction boundaries, and follow the normal  life-cycle similar to resource local. If the  is created in the context of an active JTA transaction, it will automatically be part of the JTA transaction and commit/rollback with the JTA transaction. Otherwise it must join a JTA transaction to commit/rollback using

Example JTA transaction
=Advanced=

Join Transaction
The API allows an application managed   to join the active JTA transaction context. This allows an  to be created outside the JTA transaction scope and commit its changes as part of the current transaction. This is normally used with a, or with a JSP or Servlet where an   is used in a stateful architecture. A stateful architecture is one where the server stores information on a client connection until the client's session is over, it differs from a stateless architecture where nothing is stored on the server in between client requests (each request is processed on its own).

There are pros and cons with both stateful and stateless architectures. One of the advantages with using a stateful architecture and an, is that you do not have to worry about merging objects. You can read your objects in one request, let the client modify them, and then commit them as part of a new transaction. This is where  would be used. One issue with this is that you normally want to create the  when there is no active JTA transaction, otherwise it will commit as part of that transaction. However, even if it does commit, you can still continue to use it and join a future transaction. You do have to avoid using transactional API such as  or   until you are ready to commit the transaction.

is only used with JTA managed s (JTA transaction-type in persistence.xml). For   s you can just commit the JPA transaction whenever you desire.

Retrying Transactions, Handling Commit Failures
Sometimes it is desirable to handle persistence errors, and recover and retry transactions. This normally requires a lot of application knowledge to know what failed, what state the system is in, and how to fix it.

Unfortunately JPA does not provide a direct way of handling commit failures or error handling. When a transaction commit fails, the transaction is automatically rolled back, and the persistence context cleared, and all managed objects detached. Not only is there no way to handle a commit failure, but if any error occurs in an query before the commit, the transaction will be marked for rollback, so there is no real way to handle any error. This is because any query could potentially change the state of the database, so JPA does not know if the database is in an invalid or inconsistent state so must rollback the transaction. As well if the commit fails, the state of the objects registered in the persistence context may also be inconsistent (such as partially committed objects having their optimistic lock versions incremented), so the persistence context is cleared to avoid further errors.

Some JPA providers may provide extended API to allow handling commit failures, or handling errors on queries.

There are some methods to generically handle commit failures and other errors. Error handling in general is normally easier when using  transactions, and not when using JTA transactions.

One method of error handling is to call  for each managed object after the commit fails into a new , then try to commit the new. One issue may be that any ids that were assigned, or optimistic lock versions that were assigned or incremented may need to be reset. Also, if the original  was , any objects that were in use would still become detached, and need to be reset.

Another more involved method to error handling is to always work with a non-transactional. When it's time to commit, a new  is created, the non-transactional objects are merged into it, and the new   is committed. If the commit fails, only the state of the new  may be inconsistent, the original   will be unaffected. This can allow the problem to be corrected, and the  re-merged into another new. If the commit is successful any commit changes can be merged back into the original, which can then continue to be used as normal. This solution requires a fair bit of overhead, so should only be used if error handling is really required, and the JPA provider provides no alternatives.

Nested Transactions
JPA and JTA do not support nested transactions.

A nested transaction is used to provide a transactional guarantee for a subset of operations performed within the scope of a larger transaction. Doing this allows you to commit and abort the subset of operations independently of the larger transaction.

The rules to the usage of a nested transaction are as follows:

While the nested (child) transaction is active, the parent transaction may not perform any operations other than to commit or abort, or to create more child transactions.

Committing a nested transaction has no effect on the state of the parent transaction. The parent transaction is still uncommitted. However, the parent transaction can now see any modifications made by the child transaction. Those modifications, of course, are still hidden to all other transactions until the parent also commits.

Likewise, aborting the nested transaction has no effect on the state of the parent transaction. The only result of the abort is that neither the parent nor any other transactions will see any of the database modifications performed under the protection of the nested transaction.

If the parent transaction commits or aborts while it has active children, the child transactions are resolved in the same way as the parent. That is, if the parent aborts, then the child transactions abort as well. If the parent commits, then whatever modifications have been performed by the child transactions are also committed.

The locks held by a nested transaction are not released when that transaction commits. Rather, they are now held by the parent transaction until such a time as that parent commits.

Any database modifications performed by the nested transaction are not visible outside of the larger encompassing transaction until such a time as that parent transaction is committed.

The depth of the nesting that you can achieve with nested transaction is limited only by memory.

Transaction Isolation
/Runtime