-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Best practice for Hibernate session handling in AJAX Web app
PostPosted: Mon Mar 20, 2006 2:38 pm 
Newbie

Joined: Sun Feb 05, 2006 6:24 pm
Posts: 7
I'm working on a Hibernate integration in ZK zk1.sourceforge.net... I have used an approach described in Enjoying Web Development with Tapestry

some basic assumptions... not realy shure...
*a Hibernate session is an expensive ressource... create it only when necessary, dont hold it after a response is sent to the client!
*a Hibernate session is not stateless and can not be pooled(?)
*creating a database connection is a time consuming process... use a connection pool!
*dont access detached Hibernate POJO's after a session is closed, since related data might not yet be retrieved from the database (lazy initialisation)!
*a hibernate session might be necessary during an request/response cycle
*a hibernate session might be necessary during an Ajax event processing thread

In a web application there are several instances , which have a meaning for a hibernate session:

*the application context - stores the only context wide Hibernate session factory (HibernateSessionCreator)
*the HttpSession - stores the HibernateSessionOwner (described later)
*the HttpRequest cycle thread cleanup - has to care for commit, stop transaction and closing of the Hibernate session
*the Ajax event thread cleanup - has also to care for commit, stop transaction and closing of the Hibernate session

The first time when a hibernate session is used the static final getHibernateSession(HtttpSession) method from class HibernateSessionProvider is called. This method check wether the HttpSession has already a HibernateSessionOwner... when not it creates one and stuff it in the HttpSession:

Code:
public class HibernateSessionProvider {
   private static final Log log = Log.lookup(HibernateSessionProvider.class);
   private static final String HIBERNATE_SESSION_OWNER = "de.infratour.web.services.DefaultSessionOwner";

   public static final Session getHibernateSession(HttpSession httpSession){
      Session hs = ((SessionOwner)httpSession.getAttribute(HIBERNATE_SESSION_OWNER)).getSession();
      if(hs==null){
         httpSession.setAttribute(HIBERNATE_SESSION_OWNER, new HibernateSessionOwner(httpSession));
         if(httpSession.getAttribute(HIBERNATE_SESSION_OWNER)==null){
            log.error("Could not install Hibernate Session Owner");
            throw new RuntimeException("HibernateSessionProvider: Could not install Hibernate Session Owner");
         }
         hs = ((SessionOwner)httpSession.getAttribute(HIBERNATE_SESSION_OWNER)).getSession();
         if(hs == null){
            log.error("Could not retrieve Hibernate Session");
            throw new RuntimeException("HibernateSessionProvider: Could not retrieve Hibernate Session");
         }
      }
      return hs;
   }
}


The constructor checks wether the web application context has already a HibernateSessionCreator instance... when not it creates one and stuffs it in the application context:

Code:

public class HibernateSessionOwner implements SessionOwner {
   private static final String HIBERNATE_SESSION_CREATOR = "de.infratour.web.services.DefaultSessionCreator";
   private static final Log log = Log.lookup(HibernateSessionCreator.class);

   private SessionCreator creator;
   private Session hibernateSession;
   private Transaction tx;
   private boolean isToRollback;

   public HibernateSessionOwner(HttpSession httpSession) {
      creator = (SessionCreator)httpSession
                .getServletContext()
                .getAttribute(HIBERNATE_SESSION_CREATOR);
      
      if(creator == null){
         httpSession.getServletContext()
         .setAttribute(HIBERNATE_SESSION_CREATOR, new HibernateSessionCreator());
         creator = (SessionCreator)httpSession
                      .getServletContext()
                      .getAttribute(HIBERNATE_SESSION_CREATOR);
         if(creator == null){
            log.error("Could not install SessionCreatorService" );
            throw new RuntimeException("Could not install SessionCreatorService");
         }
      }
   }
   
   public Session getSession() {
      if (hibernateSession == null) {
         hibernateSession = creator.createSession();
         if (tx == null) {
            tx = hibernateSession.beginTransaction();
            isToRollback = false;
         }
      }
      return hibernateSession;
   }

   public void threadDidDiscardService() {
      if (hibernateSession != null) {
         try {
            endTransaction();
         } finally {
            hibernateSession.close();
            hibernateSession = null;
         }
      }
   }

   public void setToRollback() {
      isToRollback = true;
   }

   public void endTransaction() {
      if (tx != null) {
         try {
            if (isToRollback) {
               tx.rollback();
            } else {
               tx.commit();
            }
         } catch (RuntimeException e) {
            tx.rollback();
            throw e;
         } finally {
            tx = null;
         }
      }
   }

        //not needed
   public void setSessionCreator(SessionCreator creator) {
      this.creator = creator;
   }
}



The created HibernateSessionCreator is now avaiable in the application:

Code:

public class HibernateSessionCreator implements SessionCreator {
   private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
   /** A session attribute. */
   
   private static final Log log = Log.lookup(HibernateSessionCreator.class);
   
   private SessionFactory sessionFactory;

   public HibernateSessionCreator() {
      Configuration cfg = new Configuration();
      cfg.configure(CONFIG_FILE_LOCATION);
      sessionFactory = cfg.buildSessionFactory();

      if (sessionFactory == null){
         log.error("Could not crate SessionFactory" );
         throw new RuntimeException("Could not crate SessionFactory");
      }
   }

   public Session createSession() {
      return sessionFactory.openSession();
   }
}


Now the Hibernate session can be created, is delivered and stored in the HibernateSessionOwner and provided to the application by the static final getHibernateSesion()... every instance, which needs a hibernate session during the same request or Ajax event thread gets the same hibernate session from the SessionOwner...

When a request or ajax event thread cycle ends the HibernateSessionOwner.threadDidDiscardService() is called, which cares for commit, end of transaction and closing of the Hibernate Session:

Code:
public class HibernateSessionEventThreadCleanup implements EventThreadCleanup{
   private static final String HIBERNATE_SESSION_OWNER = "de.infratour.web.services.DefaultSessionOwner";

   public void cleanup(Component comp, Event event) {
      ((HibernateSessionOwner)event.getPage().getSession()
      .getAttribute(HIBERNATE_SESSION_OWNER))
      .threadDidDiscardService();
   }
}


Is the described approach a good solution? are my assumptions ok?
What is more expensive? create a new Hibernate session and close it every time the conrol is back to the client, or hold a Hibernate session for a longer time (timeout, HttpSession lifecycle...)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 20, 2006 3:19 pm 
Expert
Expert

Joined: Mon Jan 09, 2006 5:01 pm
Posts: 311
Location: Sacramento, CA
hibernate session should be thought of as a "single-unit-of-work". One or more transactions can live in a session.

Usually there is a create new and release of session per webrequest, however if you have long-running-transactions that require multiple request to complete the transaction (shopping cart and browsing inventory come to mind) then you should probably save the session in a http session for that user-browser. Idea being you may not want the database to change the price on someone while they are checking out...meaning as they browse and add to shopping cart they are building up cached objects that should be consistent throughout their shopping on your website.

Keep in mind that hibernate's 1st level cache is mantained in the "Session" so that if you keep it around in the user session it will be hitting "cached" objects (unless you call session.evict* or session.refresh() functions.).

Using connection pools are always a good idea - except for one caveat to keep in mind - often times database resources like cursors aren't actually closed upon session.close() - it just returns connection to pool...and in this case (ie w/ Oracle) you should make sure to enclose stored procedure queries with transaction. Seems weird to me, but this is what is needed to free up cursors returned from (Oracle's) function call (in a stored-proc).

Lastly - hibernate has 2nd level cache that you have to configure (if you want) to take advantage of. The 2nd level basically supports the following: queries, objects w/load&get, and collections/associations and provides cached objects across Sessions and SessionFactories, and Clusters.

_________________
-JT

If you find my replies helpful, please rate by clicking 'Y' on them. I appreciate it.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 20, 2006 5:07 pm 
Expert
Expert

Joined: Mon Jan 09, 2006 5:01 pm
Posts: 311
Location: Sacramento, CA
one other thing - make sure to read up pg 122-125 of the reference.pdf

http://www.hibernate.org/hib_docs/v3/reference/en/pdf/hibernate_reference.pdf

It talks about this in greater details.

_________________
-JT

If you find my replies helpful, please rate by clicking 'Y' on them. I appreciate it.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 20, 2006 5:31 pm 
Newbie

Joined: Sun Feb 05, 2006 6:24 pm
Posts: 7
thanks!!!

i will have a look to this...

so a session is not as expensive as i expected and could remain for the session life time, but a transaction should be closed when the control is back to the client... as i understand the ZK framework each ajax event for a session will be handled after the other... so one non thread safe session per HttpSession sounds ok...

what about the connection, when the session remains for the lifetime of the HttpSession? Is the connection given back to the pool when the transaction has ended, or when the session is closed?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 20, 2006 6:05 pm 
Expert
Expert

Joined: Mon Jan 09, 2006 5:01 pm
Posts: 311
Location: Sacramento, CA
think of the Hibernate Session as a database connection that has objects cached during that session only. So if it makes sense to reuse your Session for your application and you have read and understand the warnings as described in the reference.pdf, then HttpSession per Hib Session is OK - Yet it reads like a perscription drug warning label.. don't use if you are doing...xyz or have this...or can't do that... etc. So make sure you understand it. So back to expensive - it is as expensive as creating a new database connection for the most part. thus using connection pooling is recommended.

If you want to cache READ-ONLY refererence data, do this in L2 cache with Query. This way the cache is shared by all Sessions and is consistent .

Again, I would only cache (in HttpSession) Hib Session for transactions that required multiple HttpRequests.

_________________
-JT

If you find my replies helpful, please rate by clicking 'Y' on them. I appreciate it.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.