Hibernate.orgCommunity Documentation
The most important point about Hibernate and concurrency control is that it is easy to understand. Hibernate directly uses JDBC connections and JTA resources without adding any additional locking behavior. It is recommended that you spend some time with the JDBC, ANSI, and transaction isolation specification of your database management system.
Hibernate does not lock objects in memory. Your application can expect the behavior as
defined by the isolation level of your database transactions. Through
Session
, which is also a transaction-scoped cache, Hibernate
provides repeatable reads for lookup by identifier and entity queries and not
reporting queries that return scalar values.
In addition to versioning for automatic optimistic concurrency control, Hibernate also
offers, using the
SELECT FOR UPDATE
syntax, a (minor) API for pessimistic locking of rows. Optimistic concurrency control and
this API are discussed later in this chapter.
The discussion of concurrency control in Hibernate begins with the granularity of
Configuration
, SessionFactory
, and
Session
, as well as database transactions and long conversations.
First, let's define a unit of work. A unit of work is a design pattern described by Martin Fowler as “ [maintaining] a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. ”[PoEAA] In other words, its a series of operations we wish to carry out against the database together. Basically, it is a transaction, though fulfilling a unit of work will often span multiple physical database transactions (see Section 13.1.2, “Long conversations”). So really we are talking about a more abstract notion of a transaction. The term "business transaction" is also sometimes used in lieu of unit of work.
Do not use the session-per-operation antipattern:
do not open and close a Session
for every simple database call in
a single thread. 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. This also means that auto-commit after every single
SQL statement is useless in an application as this mode is intended for ad-hoc SQL
console work. Hibernate disables, or expects the application server to disable,
auto-commit mode immediately. Database transactions are never optional. All
communication with a database has to occur inside a transaction. Auto-commit behavior for reading data
should be avoided, as many small transactions are unlikely to perform better than
one clearly defined unit of work. The latter is also more maintainable
and extensible.
The most common pattern in a multi-user client/server application is
session-per-request. In this model, a request from the client
is sent to the server, where the Hibernate persistence layer runs. A new Hibernate
Session
is opened, and all database operations are executed in this unit
of work. On completion of the work, and once the response for the client has been prepared,
the session is flushed and closed. Use a single database transaction to
serve the clients request, starting and committing it when you open and close the
Session
. The relationship between the two is one-to-one and this
model is a perfect fit for many applications.
The challenge lies in the implementation. Hibernate provides built-in management of
the "current session" to simplify this pattern. Start a
transaction when a server request has to be processed, and end the transaction
before the response is sent to the client. Common solutions are ServletFilter
, AOP interceptor with a
pointcut on the service methods, or a proxy/interception container. An EJB container
is a standardized way to implement cross-cutting aspects such as transaction
demarcation on EJB session beans, declaratively with CMT. If you
use programmatic transaction demarcation, for ease of use and code portability use the Hibernate Transaction
API shown later in this chapter.
Your application code can access a "current session" to process the request
by calling sessionFactory.getCurrentSession()
.
You will always get a Session
scoped
to the current database transaction. This has to be configured for either
resource-local or JTA environments, see Section 2.3, “Contextual sessions”.
You can extend the scope of a Session
and
database transaction until the "view has been rendered". This is especially useful
in servlet applications that utilize a separate rendering phase after the request
has been processed. Extending the database transaction until view rendering, is achieved by implementing
your own interceptor. However, this will be difficult
if you rely on EJBs with container-managed transactions. A
transaction will be completed when an EJB method returns, before rendering of any
view can start. See the Hibernate website and forum for tips and examples relating to
this Open Session in View pattern.
The Session
caches every object that is in a persistent state (watched
and checked for dirty state by Hibernate). If you keep it open for a long time or simply load too
much data, it will grow endlessly until you
get an OutOfMemoryException. One solution is to call clear()
and evict()
to manage the Session
cache, but you should consider a
Stored Procedure if you need mass data operations. Some solutions are shown in
Chapter 15, Batch processing. Keeping a Session
open for the duration
of a user session also means a higher probability of stale data.
Ending a Session
usually involves four distinct phases:
// 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();
}
You do not have to flush()
the Session
explicitly:
the call to commit()
automatically triggers the synchronization depending
on the FlushMode for the session.
A call to close()
marks the end of a session. The main implication
of close()
is that the JDBC connection will be relinquished by the
session. This Java code is portable and runs in both non-managed and JTA environments.
As outlined earlier, a much more flexible solution is Hibernate's built-in "current session" context management:
// Non-managed environment idiom with getCurrentSession()
try {
factory.getCurrentSession().beginTransaction();
// do some work
...
factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // or display error message
}
You will not see these code snippets in a regular 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
RuntimeException
(and usually can only clean up and exit), are in
different layers. The current context management by Hibernate can significantly
simplify this design by accessing a SessionFactory
.
Exception handling is discussed later in this chapter.
You should select org.hibernate.transaction.JDBCTransactionFactory
,
which is the default, and for the second example select "thread"
as your
hibernate.current_session_context_class
.
// 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();
}
// BMT idiom with getCurrentSession()
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work on Session bound to transaction
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
JDBCConnectionException
: indicates an error
with the underlying JDBC communication.
SQLGrammarException
: indicates a grammar
or syntax problem with the issued SQL.
ConstraintViolationException
: indicates some
form of integrity constraint violation.
GenericJDBCException
: a generic exception
which did not fall into any of the other categories.
// foo is an instance loaded earlier by the old session
Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
foo.setProperty("bar");
session.flush(); // Only for last transaction in conversation
t.commit(); // Also return JDBC connection
session.close(); // Only for last transaction in conversation
Hibernate will always use the locking mechanism of the database; it never lock objects in memory.
The "explicit user request" is expressed in one of the following ways:
Copyright © 2004 Red Hat, Inc.