Open Session in View: The Problem
Open Session in View: The Problem
Open Session in View: The Problem
Sessions and transactions is required reading to understand this pattern. This page
describes Hibernate 3.1.x and code shown here does not work in older versions.
The problem
A common issue in a typical (web-)application is the rendering of the view, after the main
logic of the action has been completed, and therefore, the Hibernate Session has already
been closed and the database transaction has ended. If you access detached objects that
have been loaded in the Session inside your JSP (or any other view rendering mechanism),
you might hit an unloaded collection or a proxy that isn't initialized. The exception you get is:
LazyInitializationException: Session has been closed (or a very similar message). Of course,
this is to be expected, after all you already ended your unit of work.
A first solution would be to open another unit of work for rendering the view. This can easily
be done but is usually not the right approach. Rendering the view for a completed action is
supposed to be inside the first unit of work, not a separate one. The solution, in two-tiered
systems, with the action execution, data access through the Session, and the rendering of
the view all in the same virtual machine, is to keep the Session open until the view has been
rendered.
Using an interceptor
If you implement your Session handling with Hibernates built-in support for automatic
Session context management, see Sessions and transactions, you have half of the code for
this already. Now you only need some kind of interceptor that runs after the view has been
rendered, and that will then commit the database transaction, hence close the Session. In
other words, in most applications you need the following: when an HTTP request has to be
handled, a new Session and database transaction will begin. Right before the response is
send to the client, and after all the work has been done, the transaction will be committed,
and the Session will be closed.
A good standard interceptor in a servlet container is a ServletFilter. It's rather trivial to put
some lines into a custom filter that runs on every request and before every response, taken
from CaveatEmptor:
try {
log.debug("Starting a database transaction");
sf.getCurrentSession().beginTransaction();
// Rollback only
ex.printStackTrace();
try {
if (sf.getCurrentSession().getTransaction().isActive()) {
log.debug("Trying to rollback database transaction after exception");
sf.getCurrentSession().getTransaction().rollback();
}
} catch (Throwable rbEx) {
log.error("Could not rollback transaction after exception!", rbEx);
}
If you combine this filter with the automatic Session context support, writing a DAO becomes
as trivial as this:
Session currentSession;
public ItemDAO() {
currentSession = HibernateUtil.getSessionFactory().getCurrentSession();
}
Now your application controllers can use the DAOs and don't have to bother at all with
sessions or transactions, e.g. in your servlets:
return "success";
}
To enable the filter to run for all Http requests, add this to your web.xml configuration file:
<filter>
<filter-name>HibernateFilter</filter-name>
<filter-class>my.package.HibernateThreadFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
If you don't want to start a Hibernate Session (and a transaction, which obtains a database
connection from the pool) for every Http request, adjust the filter mapping to an appropriate
URL pattern (e.g. only URLs that require database access). Or, add a switch to the filter that
computes if a database session/transaction is needed, based on some arbitrary criteria (e.g.
request parameter).
Caveat: Since the Session is flushed after the view has been rendered, database exceptions
might occur after a successful output has been generated. If you use plain JSP and servlets
the page output will be rendered into a buffer, usually 8K. If the buffer is full, it is flushed
to the client browser! So, your user might see a successful page (200 OK) but in fact an
exception occurred. To avoid this, don't render into the servlet engine buffer or increase the
buffer size to a safe value. Most web frameworks avoid this issue by not rendering into the
standard servlet engine buffer, but into their own.
org.hibernate.classic.Session currentSession;
try {
log.debug("Flushing Session");
currentSession.flush();
} else {
// Cleanup
log.debug("Unbinding Session after exception");
currentSession = ManagedSessionContext.unbind(sf);
This filter is transparent for the rest of your application, no DAO or any other code that
uses the "current" Session has to be changed. However, at some point the conversation
has to end, and the Session has to be finally flushed and closed. The example above uses
a special marker flag that has to be present in the request scope. You could set this flag
during processing of the request, based on some arbitrary criteria. Maybe you have another
interceptor layer for Conversation demarcation? Or a workflow engine?
However, this kind of access is non-transactional. You need to enable the auto-commit
mode in Hibernate's configuration for this. If, and only if you configured Hibernate to enable
auto-commit mode behavior, does Hibernate set the connection into auto-commit mode after
it obtains it from the pool, for the single operation, and returns it to the pool with close() on
the JDBC Connection object afterwards.
Also note that non-transactional access will/might work if you forget to enable the auto-
commit mode in the Hibernate configuration. If you did not enable auto-commit mode in
Hibernate, the connection is in whatever mode it is by default after obtained from the pool,
and returned there without commit or rollback. The behavior is then undefined. Never do
this, as the JDBC specification does not say what happens on close() with any potentially
pending transaction (yes, it is possible that a database transaction will be started implicitly
when Hibernate obtains the connection from the pool).
In fact, consider any non-transactional data access (without JTA/EJBs) a complete anti-
pattern, as there is no performance, scalability, or other benefit to be gained from it. Some
frameworks that rely on Hibernate also rely on this anti-pattern, avoid them. Always set clear
transaction boundaries to group your statements into units of work. You can consider using
two transactions, one for executing the event, one for rendering the view, with the same
Session.
If Hibernate would, hidden from the developer and outside of any transaction demarcation,
start random database connections and transactions, why have transaction demarcation at
all? What happens when Hibernate opens a new database connection to load a collection,
but the owning entity has been deleted meanwhile? (Note that this problem does not
appear with the two-transaction strategy as described above - the single Session provides
repeatable reads for entities.) Why even have a service layer when every object can be
retrieved by simply navigating to it? How much memory should be consumed by this and
which objects should be evicted first? All of this leads to no solution, because Hibernate is
a service for online transaction processing (and certain kinds of batch operations) and not a
"streaming objects from some persistent data store in undefined units of work"-service. Also,
in addition to the n+1 selects problem, do we really need an n+1 transaction and connection
problem?
The solution for this issue is of course proper unit of work demarcation and design,
supported by possibly an interception technique as shown in the pattern here, and/or the
correct fetch technique so that all required information for a particular unit of work can be
retrieved with minimum impact, best performance, and scalability.
Note: Some people still believe that this pattern creates a dependency between the
presentation layer and Hibernate. It does not, see this thread on the forum.