Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

Tuesday, October 05, 2010

Why Hibernate Session Management needs careful analysis and implementation

What is the most complicated part in Hibernate? I would think it is the session handling. It is quite a confusing topic with a lot of material everywhere, but a lot of them confusing at the best.

I have been Stuggling with the session management for a little time now, with some hibernate errors thrown in every now and then like the ones below

 - Illegal attempt to associate a collection with two open sessions
 - a different object with the same identifier value was already associated with the session
 - LazyInitializationException: could not initialize proxy - no Session Data Access.
 - Many more -----

PHEW !!!.

I decided to take a lot more in-depth view of session handling and after reading some of the articles and books by Gavin King, finally arrived at some conclusions. Then I wrote test implementations for all the patterns and analyzed their behavior. So far the experiments has given me a good stable implementation of session handling so wanted to write a few words about what I found during the my experiments with hibernate.

The EntityManager/Persistence Context (Read Session in Hibernate)

The JPA entity manager is nothing but the session in pure Hibernate terms. I am not going to make an attempt to explain what the session does, but the following features are atmost important.

- Session is the Level 1 cache of Hibernate
- It is a cache of managed entity instances
- It defines the scope of a POJO's persistent Identity

Why Session management is so critical ?

Hibernate believes in Lazy loading (and rightly so). It is the default behavior of the O/R mapper and it tries to defer the loading of a associated Object/Collection till the last moment it could afford. This has the following implications

- If you have many-to-one or many-to-many relationships defined, hibernate will to proxy all of them when the base object is fetched. This means that all the associated objects are not populated by default (Unless you force it to be loaded by a lazy="false" on your XML definition) . It holds only a reference to the IDs of the associated objects making the collection (There are some exceptions to this as to what API was called to get the entity in to persistent state Please see documentation of get() and load() methods)

- The session has to be alive for Hibernate to intialize any of the associated objects it proxied during the initial fetch. Which means that you would get a "LazyInitializationException" if the session is closed and then you call "Entity.getAssociatedCollection()"

Patterns in session management and their applications

session-per-operation:

This is the simplest one of all to understand - In this pattern the session spans only a single operation from the database. The sample code will look like the following

Session sess = factory.openSession();
Transaction tx;
try {
     tx = sess.beginTransaction();
    
     //do a single DB operation
     ...
     tx.commit();
}
catch (Exception e) {
     if (tx!=null)
    tx.rollback();
     throw e;
}
finally {
     sess.close();
}


Though very simple in its implementation, this approach suffers from some serious drawbacks. It absolutely does not use any of Hibernate's lazy loading or L1 cache capabilities. It will force you to agressively load almost all the associated objects on the tree. Mostly this is an anti-pattern and should be avoided as far as possible.

session-per-request:

This is an extension of the session-per-operation pattern, In this pattern the session spans multiple related operations from the database. This approach works prertty good for Client Server applications. the session is closed immediately after the db operation is completed. A sample of this code is shown below.

Session sess = factory.openSession(); 
Transaction tx;
try {
     tx = sess.beginTransaction();
     
     //do multiple related DB operations, Debit account A, Credit Account B etc.
        
     ...
     tx.commit();
}
catch (Exception e) {
     if (tx!=null) 
    tx.rollback();
     throw e;
}
finally {
     sess.close();
}

This pattern though betters the Session-per-operation, is still sub optimal for Web applications where a interaction with the user can span multiple db operations and round trips between Client and Server. The same logic is extended in the "session-per-request-with-detached-objects" pattern to derive some more use of the objects loaded.

session-per-request-with-detached-objects:

Session per request with detached objects extends the above pattern with the promise of "re attaching" the entity back on to a new session when the user has completed modifications on the same. This is a really powerful feature of Hibernate. This really means that you could use your domain POJOs as carriers of information to other layers of the application in a "detached" form. Hibernate makes sure that there is no DB connection maintained durng the think time and also makes sure that the object is re attached to the new session when it is sent back. Amazing and nice for a web app !

Now lets examine this in a little more detail:

As in the session-per-request pattern, the session-per-request-with-detached-objects pattern also uses the session for the duration of multiple related db operations. This pattern relies on implementing a partly "custom aggressive fetch" strategy. As the session gets closed at the end of the DB operations, the code following cannot be relying on the session to initialize the proxied data on the Entity. This means that the designer has to carefully decide what is the granularity of the fetch needed for the "conversation" which is currently taking place.

There are multiple ways to control the depth of the fetch tree. One option is to make the lazy="false" setting on the "entity.hbm.xml" file for the required associations. But this is not a great option unless you are absolutely sure that this entity needs to be always fetched with the associations. A designer has to be extremely careful about setting this option as the associated tree could grow exponentially and could strangle your machine to death.

The better way I have seen is to programatically control the depth of the fetch by using the "initialize" method of the Hibernate Class. In this approach, the code fetching the Entity looks like the following

Transaction tx = null;
long entityIdentifier = 1234;
try
{
    Session hiberSession = HibernateUtil.currentSession();        
    tx = hiberSession.beginTransaction();

    Entity entity = (Entity) hiberSession.get(Entity.class, entityIdentifier);
            
    // initialize the mandatory collections 
            
    Hibernate.initialize(entity.getAttributeValueSet()); //Attribute value many-to many mapping - associated collection
    Hibernate.initialize(entity.getCategories()); //Product categories for this entity - associated collection
                    
            
    return entity;
}
catch (Exception e) 
{
    tx.rollback();
    return null; // OR throw this Error ----- 
}
finally
{
    HibernateUtil.closeSession();
} 

Re attaching the "Detached" Objects:

Once the session is closed after the fetch, the persistent object enters a "detached" state. A "detached" object can be made persistent again by re attaching it to a session. The easiest approach to be followed is to call the "Session.update(Entity)". Hibernate would take care of attaching the object to the current session and make it persistent again. Another option is to use the Session.merge(Entity). You have to be aware that there are some fundamental differences between the "update" and the "merge" which is explained in this document:

Key observations about the pattern:

 - It enables you to use the domain objects as transport objects as they can be detached from their persistent state
 - It forces you to fetch some of the data eagerly as the session is closed before they are accessed
 - Fine grained control of the depth of fetch is possible using programmatic constructs
 - This is suited for applications which use a lot of AJAX calls due to some reasons I have explained in the section below.

session-per-conversation:

The session per conversation pattern leverages the "extension" of the life of a session to the length of a "conversation". In a N-tier web application, the "conversation" can be often longer than the normal single DB operation. There could be considerable think time between the user actions, and the session is kept alive till such time that one complete conversation is completed.

The key advantage of this pattern is that it makes the best use of "lazy loading". The loading of all associations are delayed till such time that it becomes absolutely necessary to load them. This ideally should do away with a lot of the performance overheads with the "eager" loading of associations. The typical way of closing the session in this approach is to write a Servlet Filter where the session is closed just before the view is refreshed. A sample of such a Filter is given below. This filter can be plugged into only those pages which need to close the session when the view is rendered

public class SessionFilter implements Filter
{
    private FilterConfig filterConfig;
    private static Set<String> publicUrls = new HashSet<String>();
    
    //initializer block.
    static 
    {
         //Add urls which demarcate the boundaries of your conversation
         //publicUrls.add("CreateOffer.action");
         
    }    

    @Override
    public void destroy() 
    {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException 
    {
        try {
            chain.doFilter (request, response);
        }
        catch(Exception e) {
            // handle!
        }
        finally {
            HibernateUtil.closeSession();
        }
        
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException 
    {
        // TODO Auto-generated method stub
        
    }
    
    protected boolean isPublicResource(HttpServletRequest request) 
    {
        String resource = request.getServletPath();
        return publicUrls.contains(request.getServletPath());
                
    }
}


One of the key challenges for the designer using this pattern is to clearly identify all "conversation" boundaries. Another problem is that with Web 2.0 and the number of AJAX based applications, the boundaries of the conversation are often blurred. Or rather, sometimes, it is really difficult to identify the boundaries and sometimes it is very easy to do a wrong assessment of the boundary. The pitfall of not able to identify the boundary correctly and failing to insert the filter, can cause some really confusing Errors which are tougher to debug. Sometimes it's easier to go with the detached-objects design in applications with heavy AJAX based transactions.

Wednesday, September 22, 2010

Committed data does not appear on the new AJAX call with Hibernate

We had a scenario where we commit some records during one AJAX call in the DB using Hibernate, but the immediate next AJAX call could not show it on the same page. We had committed the data properly, but the record refuses to show up in the list.

We did not have any second level cache, so we were debugging it based on the Session closure and opening. Issue turned out to be that the proper transaction isolation level was not set in hibernate.cfg.xml file.

I am not going to go in depth on Transaction Isolation levels here, which you can read from here

The issue was resolved after we set the Isolation level with the following line in the config XML :
<property name="hibernate.connection.isolation">2</property>

An explanation on the numbers to be used for your level can be found here (look for isolation)