|
Hibernate example source code file (transactions.xml)
This example Hibernate source code file (transactions.xml) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Hibernate transactions.xml source code
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Inc or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Inc.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="transactions" revision="1">
<title>Transactions and Concurrency
<para>The most important point about Hibernate Entity Manager and
concurrency control is that it is very easy to understand. Hibernate Entity
Manager directly uses JDBC connections and JTA resources without adding any
additional locking behavior. We highly recommend you spend some time with
the JDBC, ANSI, and transaction isolation specification of your database
management system. Hibernate Entity Manager only adds automatic versioning
but does not lock objects in memory or change the isolation level of your
database transactions. Basically, use Hibernate Entity Manager like you
would use direct JDBC (or JTA/CMT) with your database resources.</para>
<para>We start the discussion of concurrency control in Hibernate with the
granularity of <literal>EntityManagerFactory, and
<literal>EntityManager, as well as database transactions and long
units of work..</para>
<para>In this chapter, and unless explicitly expressed, we will mix and
match the concept of entity manager and persistence context. One is an API
and programming object, the other a definition of scope. However, keep in
mind the essential difference. A persistence context is usually bound to a
JTA transaction in Java EE, and a persistence context starts and ends at
transaction boundaries (transaction-scoped) unless you use an extended
entity manager. Please refer to <xref
linkend="architecture-ejb-persistctxscope" /> for more information.</para>
<sect1 id="transactions-basics">
<title>Entity manager and transaction scopes
<para>A EntityManagerFactory is an expensive-to-create,
threadsafe object intended to be shared by all application threads. It is
created once, usually on application startup.</para>
<para>An EntityManager is an inexpensive,
non-threadsafe object that should be used once, for a single business
process, a single unit of work, and then discarded. An
<literal>EntityManager will not obtain a JDBC
<literal>Connection (or a Datasource) unless
it is needed, so you may safely open and close an
<literal>EntityManager even if you are not sure that data access
will be needed to serve a particular request. (This becomes important as
soon as you are implementing some of the following patterns using request
interception.)</para>
<para>To complete this picture you also have to think about database
transactions. A database transaction has to be as short as possible, to
reduce lock contention in the database. Long database transactions will
prevent your application from scaling to highly concurrent load.</para>
<para>What is the scope of a unit of work? Can a single Hibernate
<literal>EntityManager span several database transactions or is
this a one-to-one relationship of scopes? When should you open and close a
<literal>Session and how do you demarcate the database
transaction boundaries?</para>
<sect2 id="transactions-basics-uow">
<title>Unit of work
<para>First, don't use the
<emphasis>entitymanager-per-operation antipattern, that is,
don't open and close an <literal>EntityManager for every
simple database call in a single thread! Of course, the same is true for
database transactions. Database calls in an application are made using a
planned sequence, they are grouped into atomic units of work. (Note that
this also means that auto-commit after every single SQL statement is
useless in an application, this mode is intended for ad-hoc SQL console
work.)</para>
<para>The most common pattern in a multi-user client/server application
is <emphasis>entitymanager-per-request. In this model, a
request from the client is send to the server (where the JPA persistence
layer runs), a new <literal>EntityManager is opened, and all
database operations are executed in this unit of work. Once the work has
been completed (and the response for the client has been prepared), the
persistence context is flushed and closed, as well as the entity manager
object. You would also use a single database transaction to serve the
clients request. The relationship between the two is one-to-one and this
model is a perfect fit for many applications.</para>
<para>This is the default JPA persistence model in a Java EE environment
(JTA bounded, transaction-scoped persistence context); injected (or
looked up) entity managers share the same persistence context for a
particular JTA transaction. The beauty of JPA is that you don't have to
care about that anymore and just see data access through entity manager
and demarcation of transaction scope on session beans as completely
orthogonal.</para>
<para>The challenge is the implementation of this (and other) behavior
outside an EJB3 container: not only has the
<literal>EntityManager and resource-local transaction to be
started and ended correctly, but they also have to be accessible for
data access operations. The demarcation of a unit of work is ideally
implemented using an interceptor that runs when a request hits the
non-EJB3 container server and before the response will be send (i.e. a
<literal>ServletFilter if you are using a standalone servlet
container). We recommend to bind the <literal>EntityManager to
the thread that serves the request, using a
<literal>ThreadLocal variable. This allows easy access (like
accessing a static variable) in all code that runs in this thread.
Depending on the database transaction demarcation mechanism you chose,
you might also keep the transaction context in a
<literal>ThreadLocal variable. The implementation patterns for
this are known as <emphasis>ThreadLocal Session and
<emphasis>Open Session in View in the Hibernate community.
You can easily extend the <literal>HibernateUtil shown in the
Hibernate reference documentation to implement this pattern, you don't
need any external software (it's in fact very trivial). Of course, you'd
have to find a way to implement an interceptor and set it up in your
environment. See the Hibernate website for tips and examples. Once
again, remember that your first choice is naturally an EJB3 container -
preferably a light and modular one such as JBoss application
server.</para>
</sect2>
<sect2 id="transactions-basics-apptx">
<title>Long units of work
<para>The entitymanager-per-request pattern is not the only useful
concept you can use to design units of work. Many business processes
require a whole series of interactions with the user interleaved with
database accesses. In web and enterprise applications it is not
acceptable for a database transaction to span a user interaction with
possibly long waiting time between requests. Consider the following
example:</para>
<itemizedlist>
<listitem>
<para>The first screen of a dialog opens, the data seen by the user
has been loaded in a particular <literal>EntityManager and
resource-local transaction. The user is free to modify the detached
objects.</para>
</listitem>
<listitem>
<para>The user clicks "Save" after 5 minutes and expects his
modifications to be made persistent; he also expects that he was the
only person editing this information and that no conflicting
modification can occur.</para>
</listitem>
</itemizedlist>
<para>We call this unit of work, from the point of view of the user, a
long running <emphasis>application transaction. There are
many ways how you can implement this in your application.</para>
<para>A first naive implementation might keep the
<literal>EntityManager and database transaction open during
user think time, with locks held in the database to prevent concurrent
modification, and to guarantee isolation and atomicity. This is of
course an anti-pattern, a pessimistic approach, since lock contention
would not allow the application to scale with the number of concurrent
users.</para>
<para>Clearly, we have to use several database transactions to implement
the application transaction. In this case, maintaining isolation of
business processes becomes the partial responsibility of the application
tier. A single application transaction usually spans several database
transactions. It will be atomic if only one of these database
transactions (the last one) stores the updated data, all others simply
read data (e.g. in a wizard-style dialog spanning several
request/response cycles). This is easier to implement than it might
sound, especially if you use JPA entity manager and persistence context
features:</para>
<itemizedlist>
<listitem>
<para>Automatic Versioning - An entity manager
can do automatic optimistic concurrency control for you, it can
automatically detect if a concurrent modification occurred during
user think time (usually by comparing version numbers or timestamps
when updating the data in the final resource-local
transaction).</para>
</listitem>
<listitem>
<para>Detached Entities - If you decide to use
the already discussed <emphasis>entity-per-request
pattern, all loaded instances will be in detached state during user
think time. The entity manager allows you to merge the detached
(modified) state and persist the modifications, the pattern is
called
<emphasis>entitymanager-per-request-with-detached-entities.
Automatic versioning is used to isolate concurrent
modifications.</para>
</listitem>
<listitem>
<para>Extended Entity Manager - The Hibernate
Entity Manager may be disconnected from the underlying JDBC
connection between two client calls and reconnected when a new
client request occurs. This pattern is known as
<emphasis>entitymanager-per-application-transaction and
makes even merging unnecessary. An extend persistence context is
responsible to collect and retain any modification (persist, merge,
remove) made outside a transaction. The next client call made inside
an active transaction (typically the last operation of a user
conversation) will execute all queued modifications. Automatic
versioning is used to isolate concurrent modifications.</para>
</listitem>
</itemizedlist>
<para>Both
<emphasis>entitymanager-per-request-with-detached-objects and
<emphasis>entitymanager-per-application-transaction have
advantages and disadvantages, we discuss them later in this chapter in
the context of optimistic concurrency control.</para>
<para>TODO: This note should probably come later.
</sect2>
<sect2 id="transactions-basics-identity">
<title>Considering object identity
<para>An application may concurrently access the same persistent state
in two different persistence contexts. However, an instance of a managed
class is never shared between two persistence contexts. Hence there are
two different notions of identity:</para>
<variablelist spacing="compact">
<varlistentry>
<term>Database Identity
<listitem>
<para>foo.getId().equals( bar.getId() )
</listitem>
</varlistentry>
<varlistentry>
<term>JVM Identity
<listitem>
<para>foo==bar
</listitem>
</varlistentry>
</variablelist>
<para>Then for objects attached to a particular
persistence context (i.e. in the scope of an
<literal>EntityManager) the two notions are equivalent, and
JVM identity for database identity is guaranteed by the Hibernate Entity
Manager. However, while the application might concurrently access the
"same" (persistent identity) business object in two different
persistence contexts, the two instances will actually be "different"
(JVM identity). Conflicts are resolved using (automatic versioning) at
flush/commit time, using an optimistic approach.</para>
<para>This approach leaves Hibernate and the database to worry about
concurrency; it also provides the best scalability, since guaranteeing
identity in single-threaded units of work only doesn't need expensive
locking or other means of synchronization. The application never needs
to synchronize on any business object, as long as it sticks to a single
thread per <literal>EntityManager. Within a persistence
context, the application may safely use <literal>== to compare
entities.</para>
<para>However, an application that uses == outside of
a persistence context might see unexpected results. This might occur
even in some unexpected places, for example, if you put two detached
instances into the same <literal>Set. Both might have the same
database identity (i.e. they represent the same row), but JVM identity
is by definition not guaranteed for instances in detached state. The
developer has to override the <literal>equals() and
<literal>hashCode() methods in persistent classes and
implement his own notion of object equality. There is one caveat: Never
use the database identifier to implement equality, use a business key, a
combination of unique, usually immutable, attributes. The database
identifier will change if a transient entity is made persistent (see the
contract of the <literal>persist() operation). If the
transient instance (usually together with detached instances) is held in
a <literal>Set, changing the hashcode breaks the contract of
the <literal>Set. Attributes for good business keys don't have
to be as stable as database primary keys, you only have to guarantee
stability as long as the objects are in the same <literal>Set.
See the Hibernate website for a more thorough discussion of this issue.
Also note that this is not a Hibernate issue, but simply how Java object
identity and equality has to be implemented.</para>
</sect2>
<sect2 id="transactions-basics-issues">
<title>Common concurrency control issues
<para>Never use the anti-patterns
<emphasis>entitymanager-per-user-session or
<emphasis>entitymanager-per-application (of course, there are
rare exceptions to this rule, e.g. entitymanager-per-application might
be acceptable in a desktop application, with manual flushing of the
persistence context). Note that some of the following issues might also
appear with the recommended patterns, make sure you understand the
implications before making a design decision:</para>
<itemizedlist>
<listitem>
<para>An entity manager is not thread-safe. Things which are
supposed to work concurrently, like HTTP requests, session beans, or
Swing workers, will cause race conditions if an
<literal>EntityManager instance would be shared. If you
keep your Hibernate <literal>EntityManager in your
<literal>HttpSession (discussed later), you should
consider synchronizing access to your Http session. Otherwise, a
user that clicks reload fast enough may use the same
<literal>EntityManager in two concurrently running
threads. You will very likely have provisions for this case already
in place, for other non-threadsafe but session-scoped
objects.</para>
</listitem>
<listitem>
<para>An exception thrown by the Entity Manager means you have to
rollback your database transaction and close the
<literal>EntityManager immediately (discussed later in
more detail). If your <literal>EntityManager is bound to
the application, you have to stop the application. Rolling back the
database transaction doesn't put your business objects back into the
state they were at the start of the transaction. This means the
database state and the business objects do get out of sync. Usually
this is not a problem, because exceptions are not recoverable and
you have to start over your unit of work after rollback
anyway.</para>
</listitem>
<listitem>
<para>The persistence context caches every object that is in managed
state (watched and checked for dirty state by Hibernate). This means
it grows endlessly until you get an
<classname>OutOfMemoryException, if you keep it open for
a long time or simply load too much data. One solution for this is
some kind batch processing with regular flushing of the persistence
context, but you should consider using a database stored procedure
if you need mass data operations. Some solutions for this problem
are shown in <xref linkend="batch" />. Keeping a persistence context
open for the duration of a user session also means a high
probability of stale data, which you have to know about and control
appropriately.</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-demarcation">
<title>Database transaction demarcation
<para>Database (or system) transaction boundaries are always necessary. No
communication with the database can occur outside of a database
transaction (this seems to confuse many developers who are used to the
auto-commit mode). Always use clear transaction boundaries, even for
read-only operations. Depending on your isolation level and database
capabilities this might not be required but there is no downside if you
always demarcate transactions explicitly. You'll have to do operations
outside a transaction, though, when you'll need to retain modifications in
an <literal>EXTENDED persistence context.
<para>A JPA application can run in non-managed (i.e. standalone, simple
Web- or Swing applications) and managed Java EE environments. In a
non-managed environment, an <literal>EntityManagerFactory is
usually responsible for its own database connection pool. The application
developer has to manually set transaction boundaries, in other words,
begin, commit, or rollback database transactions itself. A managed
environment usually provides container-managed transactions, with the
transaction assembly defined declaratively through annotations of EJB
session beans, for example. Programmatic transaction demarcation is then
no longer necessary, even flushing the <literal>EntityManager is
done automatically.</para>
<para>Usually, ending a unit of work involves four distinct phases:
<itemizedlist spacing="compact">
<listitem>
<para>commit the (resource-local or JTA) transaction (this
automatically flushes the entity manager and persistence
context)</para>
</listitem>
<listitem>
<para>close the entity manager (if using an application-managed entity
manager)</para>
</listitem>
<listitem>
<para>handle exceptions
</listitem>
</itemizedlist>
<para>We'll now have a closer look at transaction demarcation and
exception handling in both managed- and non-managed environments.</para>
<sect2 id="transactions-demarcation-nonmanaged">
<title>Non-managed environment
<para>If an JPA persistence layer runs in a non-managed environment,
database connections are usually handled by Hibernate's pooling
mechanism behind the scenes. The common entity manager and transaction
handling idiom looks like this:</para>
<programlisting role="JAVA" language="JAVA">// Non-managed environment idiom
EntityManager em = emf.createEntityManager();
EntityTransaction tx = null;
try {
tx = em.getTransaction();
tx.begin();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if ( tx != null && tx.isActive() ) tx.rollback();
throw e; // or display error message
}
finally {
em.close();
}</programlisting>
<para>You don't have to flush() the
<literal>EntityManager explicitly - the call to
<literal>commit() automatically triggers the
synchronization.</para>
<para>A call to close() marks the end of an
<literal>EntityManager. The main implication of
<literal>close() is the release of resources - make sure you
always close and never outside of guaranteed finally block.</para>
<para>You will very likely never see this idiom in business code in a
normal application; fatal (system) exceptions should always be caught at
the "top". In other words, the code that executes entity manager calls
(in the persistence layer) and the code that handles
<literal>RuntimeException (and usually can only clean up and
exit) are in different layers. This can be a challenge to design
yourself and you should use J2EE/EJB container services whenever they
are available. Exception handling is discussed later in this
chapter.</para>
<sect3>
<title>EntityTransaction
<para>In a JTA environment, you don't need any extra API to interact
with the transaction in your environment. Simply use transaction
declaration or the JTA APIs.</para>
<para>If you are using a RESOURCE_LOCAL entity
manager, you need to demarcate your transaction boundaries through the
<literal>EntityTransaction API. You can get an
<literal>EntityTransaction through
<literal>entityManager.getTransaction(). This
<literal>EntityTransaction API provides the regular
<methodname>begin(), commit(),
<methodname>rollback() and
<methodname>isActive() methods. It also provide a way to
mark a transaction as rollback only, ie force the transaction to
rollback. This is very similar to the JTA operation
<methodname>setRollbackOnly(). When a
<literal>commit() operation fail and/or if the transaction
is marked as <literal>setRollbackOnly(), the
<literal>commit() method will try to rollback the
transaction and raise a
<literal>javax.transaction.RollbackException.
<para>In a JTA entity manager,
<literal>entityManager.getTransaction() calls are not
permitted.</para>
</sect3>
</sect2>
<sect2 id="transactions-demarcation-jta">
<title>Using JTA
<para>If your persistence layer runs in an application server (e.g.
behind EJB3 session beans), every datasource connection obtained
internally by the entity manager will automatically be part of the
global JTA transaction. Hibernate offers two strategies for this
integration.</para>
<para>If you use bean-managed transactions (BMT), the code will look
like this:</para>
<programlisting role="JAVA" language="JAVA">// BMT idiom
@Resource public UserTransaction utx;
@Resource public EntityManagerFactory factory;
public void doBusiness() {
EntityManager em = factory.createEntityManager();
try {
// do some work
...
utx.commit();
}
catch (RuntimeException e) {
if (utx != null) utx.rollback();
throw e; // or display error message
}
finally {
em.close();
}</programlisting>
<para>With Container Managed Transactions (CMT) in an EJB3 container,
transaction demarcation is done in session bean annotations or
deployment descriptors, not programatically. The
<literal>EntityManager will automatically be flushed on
transaction completion (and if you have injected or lookup the
<literal>EntityManager, it will be also closed automatically).
If an exception occurs during the <literal>EntityManager use,
transaction rollback occurs automatically if you don't catch the
exception. Since <literal>EntityManager exceptions are
<literal>RuntimeExceptions they will rollback the transaction
as per the EJB specification (system exception vs. application
exception).</para>
<para>It is important to let Hibernate EntityManager define the
<literal>hibernate.transaction.factory_class (ie not
overriding this value). Remember to also set
<literal>org.hibernate.transaction.manager_lookup_class.
<para>If you work in a CMT environment, you might also want to use the
same entity manager in different parts of your code. Typically, in a
non-managed environment you would use a <literal>ThreadLocal
variable to hold the entity manager, but a single EJB request might
execute in different threads (e.g. session bean calling another session
bean). The EJB3 container takes care of the persistence context
propagation for you. Either using injection or lookup, the EJB3
container will return an entity manager with the same persistence
context bound to the JTA context if any, or create a new one and bind it
(see <xref linkend="architecture-ejb-persistctxpropagation" /> .)
<para>Our entity manager/transaction management idiom for CMT and EJB3
container-use is reduced to this:</para>
<programlisting role="JAVA" language="JAVA">//CMT idiom through injection
@PersistenceContext(name="sample") EntityManager em;</programlisting>
<para>Or this if you use Java Context and Dependency Injection
(CDI).</para>
<programlisting role="JAVA" language="JAVA">@Inject EntityManager em;
<para>In other words, all you have to do in a managed environment is to
inject the <literal>EntityManager, do your data access work,
and leave the rest to the container. Transaction boundaries are set
declaratively in the annotations or deployment descriptors of your
session beans. The lifecycle of the entity manager and persistence
context is completely managed by the container.</para>
<para>Due to a silly limitation of the JTA spec, it is not possible for
Hibernate to automatically clean up any unclosed
<literal>ScrollableResults or Iterator
instances returned by <literal>scroll() or
<literal>iterate(). You must release the
underlying database cursor by calling
<literal>ScrollableResults.close() or
<literal>Hibernate.close(Iterator) explicitly from a
<literal>finally block. (Of course, most applications can
easily avoid using <literal>scroll() or
<literal>iterate() at all from the CMT code.)
</sect2>
<sect2 id="transactions-demarcation-exceptions">
<title>Exception handling
<para>If the EntityManager throws an exception
(including any <literal>SQLException), you should immediately
rollback the database transaction, call
<literal>EntityManager.close() (if
<methodname>createEntityManager() has been called) and
discard the <literal>EntityManager instance. Certain methods
of <literal>EntityManager will not leave
the persistence context in a consistent state. No exception thrown by an
entity manager can be treated as recoverable. Ensure that the
<literal>EntityManager will be closed by calling
<literal>close() in a finally block. Note
that a container managed entity manager will do that for you. You just
have to let the RuntimeException propagate up to the container.</para>
<para>The Hibernate entity manager generally raises exceptions which
encapsulate the Hibernate core exception. Common exceptions raised by
the <literal>EntityManager API are
<itemizedlist>
<listitem>
<para>IllegalArgumentException: something
wrong happen</para>
</listitem>
<listitem>
<para>EntityNotFoundException: an entity was
expected but none match the requirement</para>
</listitem>
<listitem>
<para>NonUniqueResultException: more than one
entity is found when calling
<methodname>getSingleResult()
</listitem>
<listitem>
<para>NoResultException: when
<methodname>getSingleResult() does not find any
matching entity</para>
</listitem>
<listitem>
<para>EntityExistsException: an existing
entity is passed to <methodname>persist()
</listitem>
<listitem>
<para>TransactionRequiredException: this
operation has to be in a transaction</para>
</listitem>
<listitem>
<para>IllegalStateException: the entity
manager is used in a wrong way</para>
</listitem>
<listitem>
<para>RollbackException: a failure happens
during <methodname>commit()
</listitem>
<listitem>
<para>QueryTimeoutException: the query takes
longer than the specified timeout (see
<literal>javax.persistence.query.timeout - this property
is a hint and might not be followed)</para>
</listitem>
<listitem>
<para>PessimisticLockException: when a lock
cannot be acquired</para>
</listitem>
<listitem>
<para>OptimisticLockException: an optimistic
lock is failing</para>
</listitem>
<listitem>
<para>LockTimeoutException: when a lock takes
longer than the expected time to be acquired
(<literal>javax.persistence.lock.timeout in
milliseconds)</para>
</listitem>
<listitem>
<para>TransactionRequiredException: an
operation requiring a transaction is executed outside of a
transaction</para>
</listitem>
</itemizedlist>
<para>The HibernateException, which wraps most of the
errors that can occur in a Hibernate persistence layer, is an unchecked
exception. Note that Hibernate might also throw other unchecked
exceptions which are not a <literal>HibernateException. These
are, again, not recoverable and appropriate action should be
taken.</para>
<para>Hibernate wraps SQLExceptions thrown while
interacting with the database in a <literal>JDBCException. In
fact, Hibernate will attempt to convert the exception into a more
meaningful subclass of <literal>JDBCException. The underlying
<literal>SQLException is always available via
<literal>JDBCException.getCause(). Hibernate converts the
<literal>SQLException into an appropriate
<literal>JDBCException subclass using the
<literal>SQLExceptionConverter attached to the
<literal>SessionFactory. By default, the
<literal>SQLExceptionConverter is defined by the configured
dialect; however, it is also possible to plug in a custom implementation
(see the javadocs for the
<literal>SQLExceptionConverterFactory class for details). The
standard <literal>JDBCException subtypes are:
<itemizedlist spacing="compact">
<listitem>
<para>JDBCConnectionException - indicates an
error with the underlying JDBC communication.</para>
</listitem>
<listitem>
<para>SQLGrammarException - indicates a grammar
or syntax problem with the issued SQL.</para>
</listitem>
<listitem>
<para>ConstraintViolationException - indicates
some form of integrity constraint violation.</para>
</listitem>
<listitem>
<para>LockAcquisitionException - indicates an
error acquiring a lock level necessary to perform the requested
operation.</para>
</listitem>
<listitem>
<para>GenericJDBCException - a generic exception
which did not fall into any of the other categories.</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1>
<title>EXTENDED Persistence Context
<para>All application managed entity manager and container managed
persistence contexts defined as such are <literal>EXTENDED. This
means that the persistence context type goes beyond the transaction life
cycle. We should then understand what happens to operations made outside
the scope of a transaction.</para>
<para>In an EXTENDED persistence context, all read only
operations of the entity manager can be executed outside a transaction
(<literal>find(), getReference(),
<literal>refresh(), detach() and read
queries). Some modifications operations can be executed outside a
transaction, but they are queued until the persistence context join a
transaction: this is the case of <literal>persist(),
<literal>merge(),
<literal>remove(). Some operations cannot be called outside a
transaction: <literal>flush(), lock(), and
update/delete queries.</para>
<sect2>
<title>Container Managed Entity Manager
<para>When using an EXTENDED persistence context with
a container managed entity manager, the lifecycle of the persistence
context is binded to the lifecycle of the Stateful Session Bean. Plus if
the entity manager is created outside a transaction, modifications
operations (persist, merge, remove) are queued in the persistence
context and not executed to the database.</para>
<para>When a method of the stateful session bean involved or starting a
transaction is later called, the entity manager join the transaction.
All queued operation will then be executed to synchronize the
persistence context.</para>
<para>This is perfect to implement the
<literal>entitymanager-per-conversation pattern. A stateful
session bean represents the conversation implementation. All
intermediate conversation work will be processed in methods not
involving transaction. The end of the conversation will be processed
inside a <literal>JTA transaction. Hence all queued operations
will be executed to the database and committed. If you are interested in
the notion of conversation inside your application, have a look at JBoss
Seam. JBoss Seam emphasizes the concept of conversation and entity
manager lifecycle and bind EJB3 and JSF together.</para>
</sect2>
<sect2>
<title>Application Managed Entity Manager
<para>Application-managed entity manager are always
<literal>EXTENDED. When you create an entity manager inside a
transaction, the entity manager automatically join the current
transaction. If the entity manager is created outside a transaction, the
entity manager will queue the modification operations. When</para>
<itemizedlist>
<listitem>
<para>entityManager.joinTransaction() is
called when a JTA transaction is active for a <literal>JTA
entity manager</para>
</listitem>
<listitem>
<para>entityManager.getTransaction().begin() is
called for a <literal>RESOURCE_LOCAL entity manager
</listitem>
</itemizedlist>
<para>the entity manager join the transaction and all the queued
operations will then be executed to synchronize the persistence
context.</para>
<para>It is not legal to call
<methodname>entityManager.joinTransaction() if no JTA
transaction is involved.</para>
</sect2>
</sect1>
<sect1 id="transactions-optimistic">
<title>Optimistic concurrency control
<para>The only approach that is consistent with high concurrency and high
scalability is optimistic concurrency control with versioning. Version
checking uses version numbers, or timestamps, to detect conflicting
updates (and to prevent lost updates). Hibernate provides for three
possible approaches to writing application code that uses optimistic
concurrency. The use cases we show are in the context of long application
transactions but version checking also has the benefit of preventing lost
updates in single database transactions.</para>
<sect2 id="transactions-optimistic-manual">
<title>Application version checking
<para>In an implementation without much help from the persistence
mechanism, each interaction with the database occurs in a new
<literal>EntityManager and the developer is responsible for
reloading all persistent instances from the database before manipulating
them. This approach forces the application to carry out its own version
checking to ensure application transaction isolation. This approach is
the least efficient in terms of database access. It is the approach most
similar to EJB2 entities:</para>
<programlisting role="JAVA" language="JAVA">// foo is an instance loaded by a previous entity manager
em = factory.createEntityManager();
EntityTransaction t = em.getTransaction();
t.begin();
int oldVersion = foo.getVersion();
Foo dbFoo = em.find( foo.getClass(), foo.getKey() ); // load the current state
if ( dbFoo.getVersion()!=foo.getVersion ) throw new StaleObjectStateException();
dbFoo.setProperty("bar");
t.commit();
em.close();</programlisting>
<para>The version property is mapped using
<literal>@Version, and the entity manager will automatically
increment it during flush if the entity is dirty.</para>
<para>Of course, if you are operating in a low-data-concurrency
environment and don't require version checking, you may use this
approach and just skip the version check. In that case, <emphasis>last
commit wins</emphasis> will be the default strategy for your long
application transactions. Keep in mind that this might confuse the users
of the application, as they might experience lost updates without error
messages or a chance to merge conflicting changes.</para>
<para>Clearly, manual version checking is only feasible in very trivial
circumstances and not practical for most applications. Often not only
single instances, but complete graphs of modified objects have to be
checked. Hibernate offers automatic version checking with either
detached instances or an extended entity manager and persistence context
as the design paradigm.</para>
</sect2>
<sect2 id="transactions-optimistic-longsession">
<title>Extended entity manager and automatic versioning
<para>A single persistence context is used for the whole application
transaction. The entity manager checks instance versions at flush time,
throwing an exception if concurrent modification is detected. It's up to
the developer to catch and handle this exception (common options are the
opportunity for the user to merge his changes or to restart the business
process with non-stale data).</para>
<para>In an EXTENDED persistence context, all
operations made outside an active transaction are queued. The
<literal>EXTENDED persistence context is flushed when executed
in an active transaction (at worse at commit time).</para>
<para>The Entity Manager is disconnected from any
underlying JDBC connection when waiting for user interaction. In an
application-managed extended entity manager, this occurs automatically
at transaction completion. In a stateful session bean holding a
container-managed extended entity manager (i.e. a SFSB annotated with
<literal>@PersistenceContext(EXTENDED)), this occurs
transparently as well. This approach is the most efficient in terms of
database access. The application need not concern itself with version
checking or with merging detached instances, nor does it have to reload
instances in every database transaction. For those who might be
concerned by the number of connections opened and closed, remember that
the connection provider should be a connection pool, so there is no
performance impact. The following examples show the idiom in a
non-managed environment:</para>
<programlisting role="JAVA" language="JAVA">// foo is an instance loaded earlier by the extended entity manager
em.getTransaction.begin(); // new connection to data store is obtained and tx started
foo.setProperty("bar");
em.getTransaction().commit(); // End tx, flush and check version, disconnect</programlisting>
<para>The foo object still knows which
<literal>persistence context it was loaded in. With
<literal>getTransaction.begin(); the entity manager obtains a
new connection and resumes the persistence context. The method
<literal>getTransaction().commit() will not only flush and
check versions, but also disconnects the entity manager from the JDBC
connection and return the connection to the pool.</para>
<para>This pattern is problematic if the persistence context is too big
to be stored during user think time, and if you don't know where to
store it. E.g. the <literal>HttpSession should be kept as
small as possible. As the persistence context is also the (mandatory)
first-level cache and contains all loaded objects, we can probably use
this strategy only for a few request/response cycles. This is indeed
recommended, as the persistence context will soon also have stale
data.</para>
<para>It is up to you where you store the extended entity manager during
requests, inside an EJB3 container you simply use a stateful session
bean as described above. Don't transfer it to the web layer (or even
serialize it to a separate tier) to store it in the
<literal>HttpSession. In a non-managed, two-tiered environment
the <literal>HttpSession might indeed be the right place to
store it.</para>
</sect2>
<sect2 id="transactions-optimistic-detached">
<title>Detached objects and automatic versioning
<para>With this paradigm, each interaction with the data store occurs in
a new persistence context. However, the same persistent instances are
reused for each interaction with the database. The application
manipulates the state of detached instances originally loaded in another
persistence context and then merges the changes using
<literal>EntityManager.merge():
<programlisting role="JAVA" language="JAVA">// foo is an instance loaded by a non-extended entity manager
foo.setProperty("bar");
entityManager = factory.createEntityManager();
entityManager.getTransaction().begin();
managedFoo = session.merge(foo); // discard foo and from now on use managedFoo
entityManager.getTransaction().commit();
entityManager.close();</programlisting>
<para>Again, the entity manager will check instance versions during
flush, throwing an exception if conflicting updates occurred.</para>
</sect2>
</sect1>
</chapter>
Other Hibernate examples (source code examples)
Here is a short list of links related to this Hibernate transactions.xml source code file:
|