You are on page 1of 11

Open Session in View

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 on page 1


• Using an interceptor on page 2
• What about three-tier environments? on page 5
• What about the extended Session pattern for long Conversations? on page 5
• I don't want to write servlet filters, they are so old school! on page 8
• How do I handle exceptions? on page 8
• Can I commit the transaction before rendering the view? on page 9
• Can I use two transactions in one Session? on page 10
• Why can't Hibernate just load objects on demand? on page 10
• This is all very difficult, can't this be done easier? on page 11

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.

Generated by Jive SBS on 2010-05-26-04:00


1
Open Session in View

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:

public class HibernateSessionRequestFilter implements Filter {

private static Log log = LogFactory.getLog(HibernateSessionRequestFilter.class);

private SessionFactory sf;

public void doFilter(ServletRequest request,


ServletResponse response,
FilterChain chain)
throws IOException, ServletException {

try {
log.debug("Starting a database transaction");
sf.getCurrentSession().beginTransaction();

// Call the next filter (continue request processing)


chain.doFilter(request, response);

// Commit and cleanup


log.debug("Committing the database transaction");
sf.getCurrentSession().getTransaction().commit();

} catch (StaleObjectStateException staleEx) {


log.error("This interceptor does not implement optimistic concurrency control!");
log.error("Your application will not work until you add compensation actions!");
// Rollback, close everything, possibly compensate for any permanent changes
// during the conversation, and finally restart business conversation. Maybe
// give the user of the application a chance to merge some of his work with
// fresh data... what you do here depends on your applications design.
throw staleEx;
} catch (Throwable ex) {

Generated by Jive SBS on 2010-05-26-04:00


2
Open Session in View

// 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);
}

// Let others handle it... maybe another interceptor for exceptions?


throw new ServletException(ex);
}
}

public void init(FilterConfig filterConfig) throws ServletException {


log.debug("Initializing filter...");
log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
sf = HibernateUtil.getSessionFactory();
}

public void destroy() {}

If you combine this filter with the automatic Session context support, writing a DAO becomes
as trivial as this:

public class ItemDAO {

Session currentSession;

public ItemDAO() {
currentSession = HibernateUtil.getSessionFactory().getCurrentSession();
}

public Item getItemById(Long itemId) {


return (Item) currentSession.load(Item.class, itemId);
}
}

Alternatively, a DAO could require that a Session is a constructor argument, so responsibility


of setting the current Session would be moved to the client of the DAO (which could be a
factory). For more information about DAOs, see Generic Data Access Objects.

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:

Generated by Jive SBS on 2010-05-26-04:00


3
Open Session in View

public String execute(HttpRequest request) {

Long itemId = request.getParameter(ITEM_ID);

ItemDAO dao = new ItemDAO();

request.setAttribute( RESULT, dao.getItemById(itemId) );

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.

Generated by Jive SBS on 2010-05-26-04:00


4
Open Session in View

What about three-tier environments?


It's clear that this pattern only makes sense if you can actually use a local Session
when rendering the view. In a three-tier environment the view might be rendered on the
presentation virtual machine, not on the service virtual machine with the business and data
access layer. Therefore, keeping the Session and transaction open is not an option. In this
case you have to send the right "amount" of data to the presentation layer, so a view can be
constructed with the Session already closed. It depends on your architecture if you better
use Detached Objects, Data Transfer Objects, or maybe a mix of both with the Command
Pattern. These options are discussed in Hibernate in Action.

What about the extended Session pattern for long


Conversations?
If you'd like to use a single Session for several database transactions, you either have to
implement it completely yourself, or you have to use the built-in "application managed"
strategy:

To enable the application-managed "current" Session strategy, set


your hibernate.current_session_context_class configuration property to
org.hibernate.context.ManagedSessionContext (or simply "managed" in Hibernate 3.2).
You can now bind and unbind the "current" Session with static methods, and control the
FlushMode and flushing manually. The Servlet filter shown earlier has to do this binding and
flushing, during a conversation:

public class HibernateSessionConversationFilter


implements Filter {

private static Log log = LogFactory.getLog(HibernateSessionConversationFilter.class);

private SessionFactory sf;

Generated by Jive SBS on 2010-05-26-04:00


5
Open Session in View

public static final String HIBERNATE_SESSION_KEY = "hibernateSession";


public static final String END_OF_CONVERSATION_FLAG = "endOfConversation";

public void doFilter(ServletRequest request,


ServletResponse response,
FilterChain chain)
throws IOException, ServletException {

org.hibernate.classic.Session currentSession;

// Try to get a Hibernate Session from the HttpSession


HttpSession httpSession =
((HttpServletRequest) request).getSession();
Session disconnectedSession =
(Session) httpSession.getAttribute(HIBERNATE_SESSION_KEY);

try {

// Start a new conversation or in the middle?


if (disconnectedSession == null) {
log.debug(">>> New conversation");
currentSession = sf.openSession();
currentSession.setFlushMode(FlushMode.NEVER);
} else {
log.debug("< Continuing conversation");
currentSession = (org.hibernate.classic.Session) disconnectedSession;
}

log.debug("Binding the current Session");


ManagedSessionContext.bind(currentSession);

log.debug("Starting a database transaction");


currentSession.beginTransaction();

log.debug("Processing the event");


chain.doFilter(request, response);

log.debug("Unbinding Session after processing");


currentSession = ManagedSessionContext.unbind(sf);

// End or continue the long-running conversation?


if (request.getAttribute(END_OF_CONVERSATION_FLAG) != null ||
request.getParameter(END_OF_CONVERSATION_FLAG) != null) {

log.debug("Flushing Session");
currentSession.flush();

log.debug("Committing the database transaction");


currentSession.getTransaction().commit();

log.debug("Closing the Session");


currentSession.close();

Generated by Jive SBS on 2010-05-26-04:00


6
Open Session in View

log.debug("Cleaning Session from HttpSession");


httpSession.setAttribute(HIBERNATE_SESSION_KEY, null);

log.debug("<<< End of conversation");

} else {

log.debug("Committing database transaction");


currentSession.getTransaction().commit();

log.debug("Storing Session in the HttpSession");


httpSession.setAttribute(HIBERNATE_SESSION_KEY, currentSession);

log.debug("> Returning to user in conversation");


}

} catch (StaleObjectStateException staleEx) {


log.error("This interceptor does not implement optimistic concurrency control!");
log.error("Your application will not work until you add compensation actions!");
// Rollback, close everything, possibly compensate for any permanent changes
// during the conversation, and finally restart business conversation. Maybe
// give the user of the application a chance to merge some of his work with
// fresh data... what you do here depends on your applications design.
throw staleEx;
} catch (Throwable ex) {
// Rollback only
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);
} finally {
log.error("Cleanup after exception!");

// Cleanup
log.debug("Unbinding Session after exception");
currentSession = ManagedSessionContext.unbind(sf);

log.debug("Closing Session after exception");


currentSession.close();

log.debug("Removing Session from HttpSession");


httpSession.setAttribute(HIBERNATE_SESSION_KEY, null);

// Let others handle it... maybe another interceptor for exceptions?


throw new ServletException(ex);
}

Generated by Jive SBS on 2010-05-26-04:00


7
Open Session in View

public void init(FilterConfig filterConfig) throws ServletException {


log.debug("Initializing filter...");
log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
sf = HibernateUtil.getSessionFactory();
}

public void destroy() {}

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?

I don't want to write servlet filters, they are so old school!


You are right, servlet filters are not really hot technology. However, they work very well as
wrap-around interceptors in web applications and there is nothing wrong with servlet filters
per se. As mentioned already, if you use a web framework that has its own interceptors,
you will probably find them more flexible. Many containers also provide custom interceptors
these days - note that if these containers do not implement a Java EE standard (such as
EJB), you are locked into a proprietary runtime environment. Finally, a very flexible approach
are AOP interceptors - see Session handling with AOP.

How do I handle exceptions?


Clearly, whenever an exception happens, the database transaction has to be rolled back.
Another rule in Hibernate is that the current Session has to be closed and discarded
immediately, it can't be re-used. Hence, this is all you have to do to handle exceptions from
a Hibernate perspective. Of course you might want to retry some unit of work if it failed, or
you might want to display custom errors. All of this is outside of the scope of Hibernate and
can be implemented in any way you like.

Generated by Jive SBS on 2010-05-26-04:00


8
Open Session in View

Can I commit the transaction before rendering the view?


Apparently, though never promoted in Hibernate documentation, some developers are using
a variation of this pattern that keeps the Session open until the view has been rendered, but
commits the database transaction before rendering of the view. Then, during rendering of
the view, unloaded proxies or collections are accessed and have to be initialized. Because
the Session is still open, this seems to work. Even the Session can be called and will do
what it is told. The Session gets a connection and executes a prepared statement for a
single operation.

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.

Generated by Jive SBS on 2010-05-26-04:00


9
Open Session in View

Can I use two transactions in one Session?


Yes, this is actually a better implementation of this pattern. One database transaction
is used to read and write data during the processing of the request event. The second
database transaction is only used to read data, during rendering of the view. No
modifications to objects are made at this point. Hence, database locks are released early in
the first transaction, allowing better scalability, and the second transaction can possibly be
optimized (e.g. some databases require read-only transaction settings for best cleanup after
transaction commit). To use two transactions you need a more powerful interceptor than a
simple servlet filter - AOP is a good choice. The JBoss Seam frameworks uses this model.

Why can't Hibernate just load objects on demand?


Every month someone has the idea that Hibernate could instead of throwing a
LazyInitializationException just open up a new connection to the database (effectively
starting a new Session) and load the collection or initialize the proxy that has been touched
on-demand. Of course, this idea, while brilliant at first, has several shortcomings that only
appear if you start to think about the consequences of ad-hoc transactional access.

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

Generated by Jive SBS on 2010-05-26-04:00


10
Open Session in View

correct fetch technique so that all required information for a particular unit of work can be
retrieved with minimum impact, best performance, and scalability.

This is all very difficult, can't this be done easier?


Hibernate can only do so much as a persistence service, the problem discussed here
is however the responsibility of the application infrastructure, or framework. The EJB3
programming model makes transaction and persistence context management very easy,
use the Hibernate EntityManager to get this API. Either run your EJBs inside a full J2EE
application server (previews available from several vendors) or in a lightweight embeddable
EJB3 container, JBoss Embeddable EJB3, in any Java environment. The JBoss Seam
framework has built-in support for automatic context management, including persistence and
conversations, with only a few annotations in your source code.

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.

Generated by Jive SBS on 2010-05-26-04:00


11

You might also like