hibernate-orm/reference/en/modules/transactions.xml

816 lines
38 KiB
XML
Raw Normal View History

<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 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>
<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, this might not be required, but there
is no downside if you always demarcate transactions explicitely).
</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 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>
<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 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>
</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. 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>
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 "reassociates" them using
<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.
This approach forces the application to carry out its own version checking to ensure
application transaction isolation. (Of course, Hibernate will still <emphasis>update</emphasis>
version numbers for you.) 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();
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>
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
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>
before waiting for user activity. 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).
</para>
<para>
<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
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
<literal>Session</literal>s is many-to-one, A <literal>Session</literal> represents a
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>
<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>