910 lines
48 KiB
XML
910 lines
48 KiB
XML
<chapter id="transactions" revision="1">
|
|
<title>Transactions And Concurrency</title>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<sect1 id="transactions-basics">
|
|
<title>Session and transaction scopes</title>
|
|
|
|
<para>
|
|
A <literal>SessionFactory</literal> is an expensive-to-create, threadsafe object
|
|
intended to be shared by all application threads. It is created once, usually on
|
|
application startup, from a <literal>Configuration</literal> instance.
|
|
</para>
|
|
|
|
<para>
|
|
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.)
|
|
</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>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?
|
|
</para>
|
|
|
|
<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 has 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 <literal>HibernateUtil</literal> helper class shown earlier in this
|
|
documentation 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>
|
|
<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 occurs. 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>
|
|
(i.e. in the scope of a <literal>Session</literal>) the two notions are equivalent, and
|
|
JVM identity for database identity is guaranteed by Hibernate. 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). 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>Session</literal>. Within a <literal>Session</literal> the application may safely use
|
|
<literal>==</literal> to compare objects.
|
|
</para>
|
|
|
|
<para>
|
|
However, an application that uses <literal>==</literal> outside of a <literal>Session</literal>,
|
|
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</literal>. 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()</literal>
|
|
and <literal>hashCode()</literal> 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 object is made
|
|
persistent. If the transient instance (usually together with detached instances) is held in a
|
|
<literal>Set</literal>, changing the hashcode breaks the contract of the <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 issue, 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 a <literal>Session</literal> instance would be shared. If you keep your
|
|
Hibernate <literal>Session</literal> in your <literal>HttpSession</literal> (discussed
|
|
later), 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. Some solutions are shown in
|
|
<xref linkend="batch"/>. 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>
|
|
|
|
<para>
|
|
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 and database capabilities this might not
|
|
be required but there is no downside if you always demarcate transactions explicitly.
|
|
</para>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
However, it is often desirable 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 actually optional, but we strongly encourage its use
|
|
unless you are in a CMT session bean.
|
|
</para>
|
|
|
|
<para>
|
|
Usually, ending a <literal>Session</literal> involves four distinct phases:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
flush the session
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
commit the transaction
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
close the session
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
handle exceptions
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
|
|
<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 usually handled by Hibernate's pooling mechanism. The session/transaction handling
|
|
idiom looks like this:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[//Non-managed environment idiom
|
|
Session sess = factory.openSession();
|
|
Transaction tx = null;
|
|
try {
|
|
tx = sess.beginTransaction();
|
|
|
|
// do some work
|
|
...
|
|
|
|
tx.commit();
|
|
}
|
|
catch (RuntimeException e) {
|
|
if (tx != null) tx.rollback();
|
|
throw e; // or display error message
|
|
}
|
|
finally {
|
|
sess.close();
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
You don't have to <literal>flush()</literal> the <literal>Session</literal> explicitly -
|
|
the call to <literal>commit()</literal> automatically triggers the synchronization.
|
|
</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.
|
|
</para>
|
|
|
|
<para>
|
|
This Java code is portable and runs in both non-managed and JTA environments.
|
|
</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 Hibernate calls (in the persistence layer) and the code that handles
|
|
<literal>RuntimeException</literal> (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>
|
|
|
|
<para>
|
|
Note that you should select <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>
|
|
(which is the default).
|
|
</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),
|
|
every datasource connection obtained by Hibernate 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) Hibernate will tell the application server to start
|
|
and end a BMT transaction if you use the <literal>Transaction</literal> API. So, the
|
|
transaction management code is identical to the non-managed environment.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[// BMT idiom
|
|
Session sess = factory.openSession();
|
|
Transaction tx = null;
|
|
try {
|
|
tx = sess.beginTransaction();
|
|
|
|
// do some work
|
|
...
|
|
|
|
tx.commit();
|
|
}
|
|
catch (RuntimeException e) {
|
|
if (tx != null) tx.rollback();
|
|
throw e; // or display error message
|
|
}
|
|
finally {
|
|
sess.close();
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
With CMT, transaction demarcation is done in session bean deployment descriptors, not programatically.
|
|
If you don't want to manually flush and close the <literal>Session</literal> yourself, just set
|
|
<literal>hibernate.transaction.flush_before_completion</literal> to <literal>true</literal>,
|
|
<literal>hibernate.connection.release_mode</literal> to <literal>after_statement</literal> or
|
|
<literal>auto</literal> and <literal>hibernate.transaction.auto_close_session</literal> to
|
|
<literal>true</literal>. Hibernate will then automatically flush and close the <literal>Session</literal>
|
|
for you. The only thing left is to rollback the transaction when an exception occurs. Fortunately, in a
|
|
CMT bean, 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. <emphasis>This
|
|
means you do not need to use the Hibernate <literal>Transaction</literal> API at all in CMT.</emphasis>
|
|
</para>
|
|
|
|
<para>
|
|
Note that you should choose <literal>org.hibernate.transaction.JTATransactionFactory</literal>
|
|
in a BMT session bean, and <literal>org.hibernate.transaction.CMTTransactionFactory</literal>
|
|
in a CMT session bean, when you configure Hibernate's transaction factory. Remember to also set
|
|
<literal>org.hibernate.transaction.manager_lookup_class</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
If you work in a CMT environment, and use automatic flushing and closing of the session, you
|
|
might also want to use the same session in different parts of your code. Typically, in a non-managed
|
|
environment you would use a <literal>ThreadLocal</literal> variable to hold the session, but a
|
|
single EJB request might execute in different threads (e.g. session bean calling another session bean).
|
|
If you don't want to bother passing your <literal>Session</literal> instance around, the
|
|
<literal>SessionFactory</literal> provides the <literal>getCurrentSession()</literal>
|
|
method, which returns a session that is bound to the JTA transaction context. This is the
|
|
easiest way to integrate Hibernate into an application! The "current" session always has
|
|
auto-flush, auto-close and auto-connection-release enabled (regardless of the above property
|
|
settings). Our session/transaction management idiom is reduced to this:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[// CMT idiom
|
|
Session sess = factory.getCurrentSession();
|
|
|
|
// do some work
|
|
...
|
|
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
In other words, all you have to do in a managed environment is call
|
|
<literal>SessionFactory.getCurrentSession()</literal>, 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. The lifecycle of the session is completely managed by
|
|
Hibernate.
|
|
</para>
|
|
|
|
<para>
|
|
There is one caveat to the use of <literal>after_statement</literal> connection release
|
|
mode. Due to a silly limitation of the JTA spec, it is not possible for Hibernate to
|
|
automatically clean up any unclosed <literal>ScrollableResults</literal> or
|
|
<literal>Iterator</literal> instances returned by <literal>scroll()</literal> or
|
|
<literal>iterate()</literal>. You <emphasis>must</emphasis> release the underlying database
|
|
cursor by calling <literal>ScrollableResults.close()</literal> or
|
|
<literal>Hibernate.close(Iterator)</literal> explicity from a <literal>finally</literal>
|
|
block. (Of course, most applications can easily avoid using <literal>scroll()</literal> or
|
|
<literal>iterate()</literal> at all from the CMT code.)
|
|
</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 treated as recoverable. Ensure that the
|
|
<literal>Session</literal> will be closed by calling <literal>close()</literal>
|
|
in a <literal>finally</literal> block.
|
|
</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 handled in one of the first frames of the method call
|
|
stack (i.e. in higher layers) 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 which are not a <literal>HibernateException</literal>. These
|
|
are, again, not recoverable and appropriate action should be taken.
|
|
</para>
|
|
|
|
<para>
|
|
Hibernate wraps <literal>SQLException</literal>s thrown while interacting with the database
|
|
in a <literal>JDBCException</literal>. In fact, Hibernate will attempt to convert the eexception
|
|
into a more meningful subclass of <literal>JDBCException</literal>. The underlying
|
|
<literal>SQLException</literal> is always available via <literal>JDBCException.getCause()</literal>.
|
|
Hibernate converts the <literal>SQLException</literal> into an appropriate
|
|
<literal>JDBCException</literal> subclass using the <literal>SQLExceptionConverter</literal>
|
|
attached to the <literal>SessionFactory</literal>. By default, the
|
|
<literal>SQLExceptionConverter</literal> is defined by the configured dialect; however, it is
|
|
also possible to plug in a custom implementation (see the javadocs for the
|
|
<literal>SQLExceptionConverterFactory</literal> class for details). The standard
|
|
<literal>JDBCException</literal> subtypes are:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
<literal>JDBCConnectionException</literal> - indicates an error
|
|
with the underlying JDBC communication.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>SQLGrammarException</literal> - indicates a grammar
|
|
or syntax problem with the issued SQL.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>ConstraintViolationException</literal> - indicates some
|
|
form of integrity constraint violation.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>LockAcquisitionException</literal> - indicates an error
|
|
acquiring a lock level necessary to perform the requested operation.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>GenericJDBCException</literal> - a generic exception
|
|
which did not fall into any of the other categories.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<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. 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</title>
|
|
|
|
<para>
|
|
In an implementation without much help from Hibernate, each interaction with the
|
|
database occurs in a new <literal>Session</literal> 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 entity EJBs.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
|
|
session = factory.openSession();
|
|
Transaction t = session.beginTransaction();
|
|
int oldVersion = foo.getVersion();
|
|
session.load( foo, foo.getKey() ); // load the current state
|
|
if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
|
|
foo.setProperty("bar");
|
|
t.commit();
|
|
session.close();]]></programlisting>
|
|
|
|
<para>
|
|
The <literal>version</literal> property is mapped using <literal><version></literal>,
|
|
and Hibernate 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 ojects have to be checked. Hibernate offers automatic
|
|
version checking with either long <literal>Session</literal> or detached instances
|
|
as the design paradigm.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="transactions-optimistic-longsession">
|
|
<title>Long session and automatic versioning</title>
|
|
|
|
<para>
|
|
A single <literal>Session</literal> instance and its persistent instances are
|
|
used for the whole application transaction. Hibernate 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 changes or to restart the business
|
|
process with non-stale data).
|
|
</para>
|
|
|
|
<para>
|
|
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, nor does it have to reload instances in every
|
|
database transaction.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
|
|
session.reconnect(); // Obtain a new JDBC connection
|
|
Transaction t = session.beginTransaction();
|
|
foo.setProperty("bar");
|
|
t.commit(); // End database transaction, flushing the change and checking the version
|
|
session.disconnect(); // Return JDBC connection ]]></programlisting>
|
|
|
|
<para>
|
|
The <literal>foo</literal> object still knows which <literal>Session</literal> it was
|
|
loaded in. <literal>Session.reconnect()</literal> obtains a new connection (or you
|
|
may supply one) and resumes the session. The method <literal>Session.disconnect()</literal>
|
|
will disconnect the session from the JDBC connection and return the connection to the pool
|
|
(unless you provided the connection). After reconnection, to force a version check on data
|
|
you aren't updating, you may call <literal>Session.lock()</literal> with
|
|
<literal>LockMode.READ</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>
|
|
If the explicit calls to <literal>disconnect()</literal> and <literal>reconnect()</literal>
|
|
are too onerous, you may instead use <literal>hibernate.connection.release_mode</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
This pattern is problematic if the <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 probably
|
|
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>
|
|
|
|
<para>
|
|
Also note that you should keep the disconnected <literal>Session</literal> close
|
|
to the persistence layer. In other words, use an EJB stateful session bean to
|
|
hold the <literal>Session</literal> and don't transfer it to the web layer (or
|
|
even serialize it to a separate tier) to store it in the <literal>HttpSession</literal>.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="transactions-optimistic-detached">
|
|
<title>Detached objects and automatic versioning</title>
|
|
|
|
<para>
|
|
Each interaction with the persistent store occurs in a new <literal>Session</literal>.
|
|
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
|
|
<literal>Session</literal> and then reattaches them using <literal>Session.update()</literal>,
|
|
<literal>Session.saveOrUpdate()</literal>, or <literal>Session.merge()</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
|
|
foo.setProperty("bar");
|
|
session = factory.openSession();
|
|
Transaction t = session.beginTransaction();
|
|
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
|
|
t.commit();
|
|
session.close();]]></programlisting>
|
|
|
|
<para>
|
|
Again, Hibernate will check instance versions during flush, throwing an
|
|
exception if conflicting updates occured.
|
|
</para>
|
|
|
|
<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-customizing">
|
|
<title>Customizing automatic versioning</title>
|
|
|
|
<para>
|
|
You may disable Hibernate's automatic version increment for particular properties and
|
|
collections by setting the <literal>optimistic-lock</literal> mapping attribute to
|
|
<literal>false</literal>. Hibernate will then no longer increment versions if the
|
|
property is dirty.
|
|
</para>
|
|
|
|
<para>
|
|
Legacy database schemas are often static and can't be modified. Or, other applications
|
|
might also access the same database and don't know how to handle version numbers or
|
|
even timestamps. In both cases, versioning can't rely on a particular column in a table.
|
|
To force a version check without a version or timestamp property mapping, with a
|
|
comparison of the state of all fields in a row, turn on <literal>optimistic-lock="all"</literal>
|
|
in the <literal><class></literal> mapping. Note that this concepetually only works
|
|
if Hibernate can compare the old and new state, i.e. if you use a single long
|
|
<literal>Session</literal> and not session-per-request-with-detached-objects.
|
|
</para>
|
|
|
|
<para>
|
|
Sometimes concurrent modification can be permitted as long as the changes that have been
|
|
made don't overlap. If you set <literal>optimistic-lock="dirty"</literal> when mapping the
|
|
<literal><class></literal>, Hibernate will only compare dirty fields during flush.
|
|
</para>
|
|
|
|
<para>
|
|
In both cases, with dedicated version/timestamp columns or with full/dirty field
|
|
comparison, Hibernate uses a single <literal>UPDATE</literal> statement (with an
|
|
appropriate <literal>WHERE</literal> clause) per entity to execute the version check
|
|
and update the information. If you use transitive persistence to cascade reattachment
|
|
to associated entities, Hibernate might execute uneccessary updates. This is usually
|
|
not a problem, but <emphasis>on update</emphasis> triggers in the database might be
|
|
executed even when no changes have been made to detached instances. You can customize
|
|
this behavior by setting <literal>select-before-update="true"</literal> in the
|
|
<literal><class></literal> mapping, forcing Hibernate to <literal>SELECT</literal>
|
|
the instance to ensure that changes did actually occur, before updating the row.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</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>
|
|
|
|
<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>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
<literal>LockMode.WRITE</literal> is acquired automatically when Hibernate updates or inserts
|
|
a row.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>LockMode.UPGRADE</literal> may be acquired upon explicit user request using
|
|
<literal>SELECT ... FOR UPDATE</literal> on databases which support that syntax.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>LockMode.UPGRADE_NOWAIT</literal> may be acquired upon explicit user request using a
|
|
<literal>SELECT ... FOR UPDATE NOWAIT</literal> under Oracle.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>LockMode.READ</literal> is acquired automatically when Hibernate reads data
|
|
under Repeatable Read or Serializable isolation level. May be re-acquired by explicit user
|
|
request.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>LockMode.NONE</literal> represents the absence of a lock. All objects switch to this
|
|
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
|
|
in this lock mode.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
The "explicit user request" is expressed in one of the following ways:
|
|
</para>
|
|
|
|
<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>
|
|
|
|
<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
|
|
a less restrictive lock than the one requested, Hibernate calls
|
|
<literal>lock()</literal> for that object.
|
|
</para>
|
|
|
|
<para>
|
|
<literal>Session.lock()</literal> performs a version number check if the specified lock
|
|
mode is <literal>READ</literal>, <literal>UPGRADE</literal> or
|
|
<literal>UPGRADE_NOWAIT</literal>. (In the case of <literal>UPGRADE</literal> or
|
|
<literal>UPGRADE_NOWAIT</literal>, <literal>SELECT ... FOR UPDATE</literal> is used.)
|
|
</para>
|
|
|
|
<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>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|