2005-02-02 17:24:20 -05:00
|
|
|
<chapter id="transactions" revision="1">
|
2004-06-03 12:31:32 -04:00
|
|
|
<title>Transactions And Concurrency</title>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
The most important point about Hibernate and concurrency control is that it is very
|
|
|
|
easy to understand. Hibernate 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 only adds automatic versioning but does not lock objects in memory or change the
|
|
|
|
isolation level of your database transactions. Basically, use Hibernate like you would
|
|
|
|
use direct JDBC (or JTA/CMT) with your database resources.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
However, in addition to automatic versioning, Hibernate also offers a (minor) API for
|
|
|
|
pessimistic locking of rows, using the <literal>SELECT FOR UPDATE</literal> syntax. This API
|
|
|
|
is discussed later in this chapter.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We start the discussion of concurrency control in Hibernate with the granularity of
|
|
|
|
<literal>Configuration</literal>, <literal>SessionFactory</literal>, and
|
|
|
|
<literal>Session</literal>, as well as database and long application transactions.
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<sect1 id="transactions-basics">
|
2005-02-02 17:24:20 -05:00
|
|
|
<title>Session and transaction scopes</title>
|
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
|
|
|
A <literal>SessionFactory</literal> is an expensive-to-create, threadsafe object
|
2005-02-02 17:24:20 -05:00
|
|
|
intended to be shared by all application threads. It is created once, usually on
|
|
|
|
application startup, from a <literal>Configuration</literal> instance.
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
A <literal>Session</literal> is an inexpensive, non-threadsafe object that should be
|
|
|
|
used once, for a single business process, a single unit of work, and then discarded.
|
|
|
|
A <literal>Session</literal> will not obtain a JDBC <literal>Connection</literal>
|
|
|
|
(or a <literal>Datasource</literal>) unless it is needed, so you may safely open
|
|
|
|
and close a <literal>Session</literal> 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.)
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
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.
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
What is the scope of a unit of work? Can a single Hibernate <literal>Session</literal>
|
|
|
|
span several database transactions or is this a one-to-one relationship of scopes? When
|
|
|
|
should you open and close a <literal>Session</literal> and how do you demarcate the
|
|
|
|
database transaction boundaries?
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
2005-02-02 17:24:20 -05:00
|
|
|
<sect2 id="transactions-basics-uow">
|
|
|
|
<title>Unit of work</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
First, don't use the <emphasis>session-per-operation</emphasis> antipattern, that is,
|
|
|
|
don't open and close a <literal>Session</literal> 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. Hibernate disables, or expects the application server to do so,
|
|
|
|
auto-commit mode immediately.)
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The most common pattern in a multi-user client/server application is
|
|
|
|
<emphasis>session-per-request</emphasis>. In this model, a request from the client
|
|
|
|
is send to the server (where the Hibernate persistence layer runs), a new Hibernate
|
|
|
|
<literal>Session</literal> 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 session is flushed and closed. You would also use a single database transaction to
|
|
|
|
serve the clients request, starting and committing it when you open and close the
|
|
|
|
<literal>Session</literal>. The relationship between the two is one-to-one and this
|
|
|
|
model is a perfect fit for many applications.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The challenge lies in the implementation: not only have the <literal>Session</literal>
|
|
|
|
and 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 server and before the response will be send (i.e.
|
|
|
|
a <literal>ServletFilter</literal>). We recommend to bind the <literal>Session</literal> to
|
|
|
|
the thread that serves the request, using a <literal>ThreadLocal</literal> 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</literal> variable. The implementation patterns for this
|
|
|
|
are known as <emphasis>ThreadLocal Session</emphasis> and <emphasis>Open Session in View</emphasis>.
|
|
|
|
You can easily extend the helper class shown in the first chapter to implement this. 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.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-basics-apptx">
|
|
|
|
<title>Application transactions</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The session-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. 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>Session</literal> and database transaction. The user is free to
|
|
|
|
modify the 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</emphasis>. There are many ways how you can implement
|
|
|
|
this in your application.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
A first naive implementation might keep the <literal>Session</literal> 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, 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 Hibernate's features:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<emphasis>Automatic Versioning</emphasis> - Hibernate can do automatic
|
|
|
|
optimistic concurrency control for you, it can automatically detect
|
|
|
|
if a concurrent modification occured during user think time.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<emphasis>Detached Objects</emphasis> - If you decide to use the already
|
|
|
|
discussed <emphasis>session-per-request</emphasis> pattern, all loaded instances
|
|
|
|
will be in detached state during user think time. Hibernate allows you to
|
|
|
|
reattach the objects and persist the modifications, the pattern is called
|
|
|
|
<emphasis>session-per-request-with-detached-objects</emphasis>. Automatic
|
|
|
|
versioning is used to isolate concurrent modifications.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<emphasis>Long Session</emphasis> - The Hibernate <literal>Session</literal> may
|
|
|
|
be disconnected from the underlying JDBC connection after the database transaction
|
|
|
|
has been committed, and reconnected when a new client request arrives. This pattern
|
|
|
|
is known as <emphasis>session-per-application-transaction</emphasis> and makes
|
|
|
|
even reattachment unnecessary. Automatic versioning is used to isolate
|
|
|
|
concurrent modifications.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Both <emphasis>session-per-request-with-detached-objects</emphasis> and
|
|
|
|
<emphasis>session-per-application-transaction</emphasis> have advantages and disadvantages,
|
|
|
|
we discuss them later in this chapter in the context of optimistic concurrency control.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-basics-identity">
|
|
|
|
<title>Considering object identity</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
An application may concurrently access the same persistent state in two
|
|
|
|
different <literal>Session</literal>s. However, an instance of a persistent class
|
|
|
|
is never shared between two <literal>Session</literal> instances. Hence there are
|
|
|
|
two different notions of identity:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<variablelist spacing="compact">
|
|
|
|
<varlistentry>
|
|
|
|
<term>Database Identity</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<literal>foo.getId().equals( bar.getId() )</literal>
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
|
|
<term>JVM Identity</term>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<literal>foo==bar</literal>
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</varlistentry>
|
|
|
|
</variablelist>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Then for objects attached to a <emphasis>particular</emphasis> <literal>Session</literal>,
|
|
|
|
the two notions are equivalent. However, while the application might concurrently access
|
|
|
|
the "same" (persistent identity) business object in two different sessions, the two
|
|
|
|
instances will actually be "different" (JVM identity).
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
This approach leaves Hibernate and the database to worry about concurrency. The
|
|
|
|
application never needs to synchronize on any business object, as long as it sticks to a
|
|
|
|
single thread per <literal>Session</literal> or object identity. Within a
|
|
|
|
<literal>Session</literal> the application may safely use <literal>==</literal> to
|
|
|
|
compare objects.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Note that an application that uses <literal>==</literal> outside of a <literal>Session</literal>,
|
|
|
|
might see unexpected results. You have to override the <literal>equals()</literal> and
|
|
|
|
<literal>hashCode()</literal> methods in your persistent classes and implement
|
|
|
|
your own notion of object equality (i.e. if you decide to keep detached instances from two
|
|
|
|
<literal>Session</literal>s in the same <literal>Set</literal>). 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 object is made
|
|
|
|
persistent, thus breaking the hashcodes for a <literal>Set</literal>. Attributes for 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</literal>. See the Hibernate website for
|
|
|
|
a more thorough discussion of this issue. Also note that this is not a Hibernate problem, but
|
|
|
|
simply how Java object identity and equality has to be implemented.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-basics-issues">
|
|
|
|
<title>Common issues</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Never use the anti-patterns <emphasis>session-per-user-session</emphasis> or
|
|
|
|
<emphasis>session-per-application</emphasis> (of course, there are rare exceptions to
|
|
|
|
this rule). 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>
|
|
|
|
A <literal>Session</literal> is not thread-safe. Things which are supposed to work
|
|
|
|
concurrently, like HTTP requests, session beans, or swing workers, will cause race
|
|
|
|
conditions. If you keep your Hibernate <literal>Session</literal> in your Http user
|
|
|
|
session, you should consider synchronizing access to your Http session. Otherwise,
|
|
|
|
a user that clicks reload fast enough may use the same <literal>Session</literal> in
|
|
|
|
two concurrently running threads.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
An exception thrown by Hibernate means you have to rollback your database transaction
|
|
|
|
and close the <literal>Session</literal> immediately (discussed later in more detail).
|
|
|
|
If your <literal>Session</literal> 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 after
|
|
|
|
rollback anyway.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
The <literal>Session</literal> caches every object that is in persistent state (watched
|
|
|
|
and checked for dirty state by Hibernate). This means it grows endlessly until you
|
|
|
|
get an OutOfMemoryException, if you keep it open for a long time or simply load too
|
|
|
|
much data. One solution for this is to call <literal>clear()</literal> and <literal>evict()</literal>
|
|
|
|
to manage the <literal>Session</literal> cache, but you most likely should consider a
|
|
|
|
Stored Procedure if you need mass data operations. Keeping a <literal>Session</literal>
|
|
|
|
open for the duration of a user session also means a high probability of stale data.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<sect1 id="transactions-demarcation">
|
|
|
|
<title>Database transaction demarcation</title>
|
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
Datatabase (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, this might not be required, but there
|
|
|
|
is no downside if you always demarcate transactions explicitely).
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
A Hibernate application can run in non-managed (i.e. standalone, simple Web- or Swing applications)
|
|
|
|
and managed J2EE environments. In a non-managed environment, Hibernate 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 himself. A managed environment
|
|
|
|
usually provides container-managed transactions, with the transaction assembly defined declaratively
|
|
|
|
in deployment descriptors of EJB session beans, for example. Programmatic transaction demarcation is
|
|
|
|
then no longer necessary, even flushing the <literal>Session</literal> is done automatically.
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
2005-02-02 17:24:20 -05:00
|
|
|
<para>
|
|
|
|
However, it is often desireable to keep your persistence layer portable. Hibernate offers a wrapper
|
|
|
|
API called <literal>Transaction</literal> that translates into the native transaction system of
|
|
|
|
your deployment environment. This API is optional (using database transactions is not!) and you don't
|
|
|
|
have to use it if database portability provided by Hibernate is all you need.
|
|
|
|
</para>
|
2004-06-03 12:31:32 -04:00
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
Usually, ending a <literal>Session</literal> involves four distinct phases:
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
flush the session
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
commit the transaction
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
close the session
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
handle exceptions
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
Flushing the session has been discussed earlier, we'll now have a closer look at transaction
|
|
|
|
demarcation and exception handling in both managed- and non-managed environments.
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
|
|
|
|
|
2005-02-02 17:24:20 -05:00
|
|
|
<sect2 id="transactions-demarcation-nonmanaged">
|
|
|
|
<title>Non-managed environment</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
If a Hibernate persistence layer runs in a non-managed environment, database connections
|
|
|
|
are either handled by Hibernate's pooling mechanism or provided by the developer (this
|
|
|
|
case has other implications, esp. with regard to caching):
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// Session sess = factory.openSession(myConnection);
|
|
|
|
Session sess = factory.openSession();
|
|
|
|
|
|
|
|
try {
|
|
|
|
// do some work
|
|
|
|
...
|
|
|
|
sess.flush();
|
|
|
|
sess.connection().commit();
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
sess.connection().rollback();
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
sess.close();
|
|
|
|
}]]></programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
We recommend, even if persistence layer portability is not your primary concern, the
|
|
|
|
<literal>Transaction</literal> API:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[Session sess = factory.openSession();
|
|
|
|
Transaction tx = null;
|
|
|
|
try {
|
|
|
|
tx = sess.beginTransaction();
|
|
|
|
|
|
|
|
// do some work
|
|
|
|
...
|
|
|
|
|
|
|
|
tx.commit();
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
if (tx != null) tx.rollback();
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
sess.close();
|
|
|
|
}]]></programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Note that you don't have to <literal>flush()</literal> the <literal>Session</literal>
|
|
|
|
explicitely, the call to <literal>commit()</literal> automatically triggers the
|
|
|
|
synchronization. This piece of code is now portable and runs in non-managed and JTA
|
|
|
|
environments. See <xref linkend="configuration-optional-transactionstrategy"/> for more
|
|
|
|
information about the <literal>Transaction</literal> API.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
A call to <literal>close()</literal> marks the end of a session. The main implication
|
|
|
|
of <literal>close()</literal> is that the JDBC connection will be relinquished by the session.
|
|
|
|
If you provided your own connection, <literal>close()</literal> returns a reference
|
|
|
|
to it, so you can manually close it or return it to the pool. Otherwise <literal>close()
|
|
|
|
</literal> returns it to the pool.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-demarcation-jta">
|
|
|
|
<title>Using JTA</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
If your persistence layer runs in an application server (e.g. behind EJB session beans),
|
|
|
|
transactions boundaries are defined in deployment descriptors. Every datasource obtained
|
|
|
|
by Hibernate will automatically be part of a global JTA transaction. Hibernate simply
|
|
|
|
joins this transaction, or if a particular session bean method has no transaction,
|
|
|
|
Hibernate will tell the application server to start and end a transaction directly.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
If you set the properties <literal>hibernate.transaction.flush_before_completion</literal>
|
|
|
|
and <literal>hibernate.transaction.auto_close_session</literal> to <literal>true</literal>,
|
|
|
|
Hibernate wil also automatically flush and close the <literal>Session</literal> for you.
|
|
|
|
The only thing left is exception handling and rollback of the database transaction.
|
|
|
|
Fortunately, even this happens automatically, since an unhandled <literal>RuntimeException</literal>
|
|
|
|
thrown by a session bean method tells the container to set the global transaction to
|
|
|
|
rollback.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
In other words, all you have to do in a managed environment is to get a <literal>Session</literal>
|
|
|
|
from the <literal>SessionFactory</literal> (usually bound to JNDI), do your data access
|
|
|
|
work, and leave the rest to the container. Transaction boundaries are set declaratively in
|
|
|
|
the deployment descriptors of your session bean.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-demarcation-exceptions">
|
|
|
|
<title>Exception handling</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
If the <literal>Session</literal> throws an exception (including any
|
|
|
|
<literal>SQLException</literal>), you should immediately rollback the database
|
|
|
|
transaction, call <literal>Session.close()</literal> and discard the
|
|
|
|
<literal>Session</literal> instance. Certain methods of <literal>Session</literal>
|
|
|
|
will <emphasis>not</emphasis> leave the session in a consistent state. No
|
|
|
|
exception thrown by Hibernate can be treaded as recoverable.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The <literal>HibernateException</literal>, which wraps most of the errors that
|
|
|
|
can occur in a Hibernate persistence layer, is an unchecked exception (it wasn't
|
|
|
|
in older versions of Hibernate). In our opinion, we shouldn't force the application
|
|
|
|
developer to catch an unrecoverable exception at a low layer. In most systems, unchecked
|
|
|
|
and fatal exceptions are only catched at the highest level of the call stack and an
|
|
|
|
error message is presented to the application user (or some other appropriate action
|
|
|
|
is taken). Note that Hibernate might also throw other unchecked exceptions (e.g.
|
|
|
|
when detecting stale data in version checks) which are not a
|
|
|
|
<literal>HibernateException</literal>.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
TODO: document new SQLException converter
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
</sect1>
|
|
|
|
|
2005-02-02 17:24:20 -05:00
|
|
|
<!--
|
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<sect1 id="transactions-optimistic">
|
|
|
|
<title>Optimistic concurrency control</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The only approach that is consistent with high concurrency and high
|
|
|
|
scalability is optimistic concurrency control with versioning. Hibernate
|
|
|
|
provides for three possible approaches to writing application code that
|
|
|
|
uses optimistic concurrency.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<sect2 id="transactions-optimistic-longsession">
|
|
|
|
<title>Long session with automatic versioning</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
A single <literal>Session</literal> instance and its persistent instances are
|
|
|
|
used for the whole application transaction.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The <literal>Session</literal> uses optimistic locking with versioning to
|
|
|
|
ensure that many database transactions appear to the application as a single
|
|
|
|
logical application transaction. The <literal>Session</literal> is disconnected
|
|
|
|
from any underlying JDBC connection when waiting for user interaction. This
|
|
|
|
approach is the most efficient in terms of database access. The application
|
|
|
|
need not concern itself with version checking or with reattaching detached
|
|
|
|
instances.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
|
|
|
|
session.reconnect();
|
|
|
|
foo.setProperty("bar");
|
|
|
|
session.flush();
|
|
|
|
session.connection().commit();
|
|
|
|
session.disconnect();]]></programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The <literal>foo</literal> object still knows which <literal>Session</literal>
|
|
|
|
it was loaded it. As soon as the <literal>Session</literal> has a JDBC connection,
|
|
|
|
we commit the changes to the object.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
This pattern is problematic if our <literal>Session</literal> is too big to
|
|
|
|
be stored during user think time, e.g. an <literal>HttpSession</literal> should
|
|
|
|
be kept as small as possible. As the <literal>Session</literal> is also the
|
|
|
|
(mandatory) first-level cache and contains all loaded objects, we can propably
|
|
|
|
use this strategy only for a few request/response cycles. This is indeed
|
|
|
|
recommended, as the <literal>Session</literal> will soon also have stale data.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-optimistic-detached">
|
|
|
|
<title>Many sessions with automatic versioning</title>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
Each interaction with the persistent store occurs in a new <literal>Session</literal>.
|
2004-06-03 12:31:32 -04:00
|
|
|
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
|
2005-02-02 17:24:20 -05:00
|
|
|
<literal>Session</literal> and then "reassociates" them using
|
2004-06-03 12:31:32 -04:00
|
|
|
<literal>Session.update()</literal> or <literal>Session.saveOrUpdate()</literal>.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
|
|
|
|
foo.setProperty("bar");
|
|
|
|
session = factory.openSession();
|
|
|
|
session.saveOrUpdate(foo);
|
|
|
|
session.flush();
|
|
|
|
session.connection().commit();
|
|
|
|
session.close();]]></programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
You may also call <literal>lock()</literal> instead of <literal>update()</literal>
|
|
|
|
and use <literal>LockMode.READ</literal> (performing a version check, bypassing all
|
|
|
|
caches) if you are sure that the object has not been modified.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="transactions-optimistic-manual">
|
|
|
|
<title>Application version checking</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Each interaction with the database occurs in a new <literal>Session</literal>
|
|
|
|
that reloads all persistent instances from the database before manipulating them.
|
2005-02-02 17:24:20 -05:00
|
|
|
This approach forces the application to carry out its own version checking to ensure
|
2004-06-03 12:31:32 -04:00
|
|
|
application transaction isolation. (Of course, Hibernate will still <emphasis>update</emphasis>
|
2005-02-02 17:24:20 -05:00
|
|
|
version numbers for you.) This approach is the least efficient in terms of database access.
|
2004-06-03 12:31:32 -04:00
|
|
|
It is the approach most similar to entity EJBs.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
|
|
|
|
session = factory.openSession();
|
|
|
|
int oldVersion = foo.getVersion();
|
|
|
|
session.load( foo, foo.getKey() );
|
|
|
|
if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
|
|
|
|
foo.setProperty("bar");
|
|
|
|
session.flush();
|
|
|
|
session.connection().commit();
|
|
|
|
session.close();]]></programlisting>
|
|
|
|
|
|
|
|
<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.
|
|
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<sect1 id="transactions-disconnection">
|
|
|
|
<title>Session disconnection</title>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
The first approach described above is to maintain a single <literal>Session</literal>
|
|
|
|
for a whole business process thats spans user think time. (For example, a servlet might
|
|
|
|
keep a <literal>Session</literal> in the user's <literal>HttpSession</literal>.) For
|
2004-06-03 12:31:32 -04:00
|
|
|
performance reasons you should
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<orderedlist spacing="compact">
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
commit the <literal>Transaction</literal> (or JDBC connection) and then
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
disconnect the <literal>Session</literal> from the JDBC connection
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
before waiting for user activity. The method <literal>Session.disconnect()</literal>
|
|
|
|
will disconnect the session from the JDBC connection and return the connection to
|
2004-06-03 12:31:32 -04:00
|
|
|
the pool (unless you provided the connection).
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
<literal>Session.reconnect()</literal> obtains a new connection (or you may supply one)
|
|
|
|
and restarts the session. After reconnection, to force a version check on data you aren't
|
2004-06-03 12:31:32 -04:00
|
|
|
updating, you may call <literal>Session.lock()</literal> on any objects that might have
|
|
|
|
been updated by another transaction. You don't need to lock any data that you
|
|
|
|
<emphasis>are</emphasis> updating.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Heres an example:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[SessionFactory sessions;
|
|
|
|
List fooList;
|
|
|
|
Bar bar;
|
|
|
|
....
|
|
|
|
Session s = sessions.openSession();
|
|
|
|
|
|
|
|
Transaction tx = null;
|
|
|
|
try {
|
|
|
|
tx = s.beginTransaction();
|
|
|
|
|
|
|
|
fooList = s.find(
|
|
|
|
"select foo from eg.Foo foo where foo.Date = current date"
|
|
|
|
// uses db2 date function
|
|
|
|
);
|
|
|
|
bar = (Bar) s.create(Bar.class);
|
|
|
|
|
|
|
|
tx.commit();
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
if (tx!=null) tx.rollback();
|
|
|
|
s.close();
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
s.disconnect();]]></programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Later on:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting><![CDATA[s.reconnect();
|
|
|
|
|
|
|
|
try {
|
|
|
|
tx = s.beginTransaction();
|
|
|
|
|
|
|
|
bar.setFooTable( new HashMap() );
|
|
|
|
Iterator iter = fooList.iterator();
|
|
|
|
while ( iter.hasNext() ) {
|
|
|
|
Foo foo = (Foo) iter.next();
|
|
|
|
s.lock(foo, LockMode.READ); //check that foo isn't stale
|
|
|
|
bar.getFooTable().put( foo.getName(), foo );
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.commit();
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
if (tx!=null) tx.rollback();
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
s.close();
|
|
|
|
}]]></programlisting>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
You can see from this how the relationship between <literal>Transaction</literal>s and
|
2005-02-02 17:24:20 -05:00
|
|
|
<literal>Session</literal>s is many-to-one, A <literal>Session</literal> represents a
|
2004-06-03 12:31:32 -04:00
|
|
|
conversation between the application and the database. The
|
|
|
|
<literal>Transaction</literal> breaks that conversation up into atomic units of work
|
|
|
|
at the database level.
|
|
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<sect1 id="transactions-locking">
|
|
|
|
<title>Pessimistic Locking</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
It is not intended that users spend much time worring about locking strategies. Its usually
|
|
|
|
enough to specify an isolation level for the JDBC connections and then simply let the
|
|
|
|
database do all the work. However, advanced users may sometimes wish to obtain
|
|
|
|
exclusive pessimistic locks, or re-obtain locks at the start of a new transaction.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Hibernate will always use the locking mechanism of the database, never lock objects
|
|
|
|
in memory!
|
|
|
|
</para>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
|
|
|
The <literal>LockMode</literal> class defines the different lock levels that may be acquired
|
|
|
|
by Hibernate. A lock is obtained by the following mechanisms:
|
|
|
|
</para>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
<literal>LockMode.WRITE</literal> is acquired automatically when Hibernate updates or inserts
|
|
|
|
a row.
|
2004-06-03 12:31:32 -04:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
<literal>LockMode.UPGRADE</literal> may be acquired upon explicit user request using
|
2004-06-03 12:31:32 -04:00
|
|
|
<literal>SELECT ... FOR UPDATE</literal> on databases which support that syntax.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
2005-02-02 17:24:20 -05:00
|
|
|
<literal>LockMode.UPGRADE_NOWAIT</literal> may be acquired upon explicit user request using a
|
2004-06-03 12:31:32 -04:00
|
|
|
<literal>SELECT ... FOR UPDATE NOWAIT</literal> under Oracle.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<literal>LockMode.READ</literal> is acquired automatically when Hibernate reads data
|
2005-02-02 17:24:20 -05:00
|
|
|
under Repeatable Read or Serializable isolation level. May be re-acquired by explicit user
|
2004-06-03 12:31:32 -04:00
|
|
|
request.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
<literal>LockMode.NONE</literal> represents the absence of a lock. All objects switch to this
|
2005-02-02 17:24:20 -05:00
|
|
|
lock mode at the end of a <literal>Transaction</literal>. Objects associated with the session
|
|
|
|
via a call to <literal>update()</literal> or <literal>saveOrUpdate()</literal> also start out
|
2004-06-03 12:31:32 -04:00
|
|
|
in this lock mode.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
|
|
|
The "explicit user request" is expressed in one of the following ways:
|
|
|
|
</para>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<itemizedlist spacing="compact">
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
A call to <literal>Session.load()</literal>, specifying a <literal>LockMode</literal>.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
A call to <literal>Session.lock()</literal>.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
A call to <literal>Query.setLockMode()</literal>.
|
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
|
|
|
If <literal>Session.load()</literal> is called with <literal>UPGRADE</literal> or
|
|
|
|
<literal>UPGRADE_NOWAIT</literal>, and the requested object was not yet loaded by
|
|
|
|
the session, the object is loaded using <literal>SELECT ... FOR UPDATE</literal>.
|
|
|
|
If <literal>load()</literal> is called for an object that is already loaded with
|
2005-02-02 17:24:20 -05:00
|
|
|
a less restrictive lock than the one requested, Hibernate calls
|
2004-06-03 12:31:32 -04:00
|
|
|
<literal>lock()</literal> for that object.
|
|
|
|
</para>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
|
|
|
<literal>Session.lock()</literal> performs a version number check if the specified lock
|
2005-02-02 17:24:20 -05:00
|
|
|
mode is <literal>READ</literal>, <literal>UPGRADE</literal> or
|
2004-06-03 12:31:32 -04:00
|
|
|
<literal>UPGRADE_NOWAIT</literal>. (In the case of <literal>UPGRADE</literal> or
|
|
|
|
<literal>UPGRADE_NOWAIT</literal>, <literal>SELECT ... FOR UPDATE</literal> is used.)
|
|
|
|
</para>
|
2005-02-02 17:24:20 -05:00
|
|
|
|
2004-06-03 12:31:32 -04:00
|
|
|
<para>
|
|
|
|
If the database does not support the requested lock mode, Hibernate will use an appropriate
|
|
|
|
alternate mode (instead of throwing an exception). This ensures that applications will
|
|
|
|
be portable.
|
|
|
|
</para>
|
|
|
|
|
2005-02-02 17:24:20 -05:00
|
|
|
</sect1>
|
|
|
|
-->
|
2004-06-03 12:31:32 -04:00
|
|
|
</chapter>
|
|
|
|
|