-->
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.  [ 7 posts ] 
Author Message
 Post subject: Efficient hibernate session management in AJAX web apps
PostPosted: Wed May 28, 2008 12:30 pm 
Newbie

Joined: Wed May 28, 2008 10:46 am
Posts: 4
Hi All,

I'm currently developing web application with some AJAX features. Main architecture is based on spring framework as a bean container, hibernate as ORM and JSF + RichFaces(which provides AJAX features) for a presentation layer.

As the application is supposed to cary rather large load of users, i was wondering how to manage hibernate session in most efficient way.

Opening session per every single transaction can't be very efficient as you have only a little use of cache which session provides. And there is of course problem with lazy loading, when JSF accesses lazy data.

Opening session for en entire request rendering is propably better solution. Session cache is used for entire page rendering, during which you can have some separete transactions and there is no problem with lazy loading. In spring framework there is actualy class OpenSessionInViewFilter which caries this functionality.

But mayby there is a better way. When using application with AJAX features, user usually performs many actions on the same page. So it seems the best way is to cary the same hibernate session over ajax requests and enclose whole page fincionality in it. In some cases (for example when displaying datatable with list of objects) this way rapidly reduces database requests, as data is taken from cache in each ajax requests. A new session is opened and a cache clears when a user display different page.

After reading "Open Session in View" (http://www.hibernate.org/43.html) i've implemented a servlet filter which stores hibernate session object in HttpSession over requests for the same page. When page is changed,current session is flushed and new session is created.

As i don't have much experience in web app development, i wonder if this aproach will actualy work. After some simple tests it seems that the number of database requests is reduced. But mayby there is something, that i'm not aware of. I'd appreciate if you let me know, what you think about this solution.

Here is a code of a filter. It works with a Spring's HibernateTransactionManager. There's also a need of creating a trivial filter mapping in Web.xml file.


Code:
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;


public class KeepSessionPerPage implements Filter{

   private static Log log = LogFactory.getLog(KeepSessionPerPage.class);
   
   public static final String DEFAULT_SESSION_FACTORY_NAME="sessionFactory";
   public static final String HIBERNATE_SESSION_KEY = "hibernateSession";
   public static final String HIBERNATE_SCOPE_KEY = "hibernateCurrentSessionScope";
   
   
    private SessionFactory sessionFactory;
    private FlushMode flushMode=FlushMode.NEVER;
   
    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException, ServletException {
      
      System.out.println("Filter...........................");
      HttpServletRequest httpRequest=(HttpServletRequest)request;
      HttpSession httpSession =((HttpServletRequest) request).getSession();
      
      Integer newResourceCode=getNewPageCode(httpRequest);
      boolean participate = false;
      boolean createNewSession=outOfCurrentScope(newResourceCode, httpSession);

      

      if (TransactionSynchronizationManager.hasResource(sessionFactory)) participate = true;
      else {         
         Session session;
         Session keptSession =(org.hibernate.classic.Session) httpSession.getAttribute(HIBERNATE_SESSION_KEY);
         if (keptSession == null) {
            log.debug("No session kept");
            
            log.info("Opening new session for page: "+httpRequest.getRequestURL());
            session = getSession(sessionFactory);
         }else if (keptSession != null && createNewSession){
            log.debug("Closing current session");
            SessionFactoryUtils.closeSession(keptSession);
            
            log.info("Opening new session for page: "+httpRequest.getRequestURL());
            session = getSession(sessionFactory);
         }else session=keptSession;
         
         TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));

      try {
         chain.doFilter(request, response);
      }finally {
         if (!participate) {
            log.debug("Unbinding session");
            SessionHolder sessionHolder =(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
            httpSession.setAttribute(HIBERNATE_SESSION_KEY, sessionHolder.getSession());
            httpSession.setAttribute(HIBERNATE_SCOPE_KEY,newResourceCode);

         }
      }
      }
   }
   
   public void init(FilterConfig filterConfig) throws ServletException {
        String sessionFactoryRef=filterConfig.getInitParameter("SessionFactoryReference");
        if (sessionFactoryRef==null) sessionFactoryRef=DEFAULT_SESSION_FACTORY_NAME;
        WebApplicationContext webAppContext=WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
        sessionFactory=(SessionFactory) webAppContext.getBean(sessionFactoryRef,SessionFactory.class);
       
        String flushModeParam=filterConfig.getInitParameter("FlushMode");
        if (flushModeParam==null);
        else if (flushModeParam.equals("ALWAYS")) flushMode=FlushMode.ALWAYS;
        else if (flushModeParam.equals("AUTO")) flushMode=FlushMode.AUTO;
        else if (flushModeParam.equals("COMMIT")) flushMode=FlushMode.COMMIT;
        else if (flushModeParam.equals("MANUAL")) flushMode=FlushMode.MANUAL;
        else if (flushModeParam.equals("NEVER")) flushMode=FlushMode.NEVER;
    }
   
   public void destroy(){}
   
   private Integer getNewPageCode(HttpServletRequest request){
      return (Integer)request.getRequestURL().toString().hashCode();
   }
   
   
   private boolean outOfCurrentScope(Integer newResourceCode, HttpSession httpSession){
      Integer currentResourceCode=(Integer)httpSession.getAttribute(HIBERNATE_SCOPE_KEY);
      
      if (currentResourceCode==null) {
         log.debug("No scope found");
         return true;
      }
      else{
         log.debug("Current page code: "+currentResourceCode);
         log.debug("New page code: "+newResourceCode);
         
         if (currentResourceCode.equals(newResourceCode)){
            log.debug("Request is in current session scope");
            return false;
         }else {
            log.debug("Request is out of current session scope");
            return true;
         }
      }
      
   }
   
   private Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
      Session session = SessionFactoryUtils.getSession(sessionFactory, true);
      session.setFlushMode(flushMode);
      return session;
   }

}

_________________
Jakub Wiśniewski


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 28, 2008 4:54 pm 
Red Hat Associate
Red Hat Associate

Joined: Mon Aug 16, 2004 11:14 am
Posts: 253
Location: Raleigh, NC
Not to dodge the question here, but Seam is designed with your exact use case in mind. It manages persistence contexts, conversations. It uses JSF and ships RichFaces. It has AJAX support. Head over to seamframework.org and look around.

Oh, and don't store Sessions in HTTPSession. Use thread-managed session contexts. See the Hibernate manual (Contextual Sessions).

EDIT: s/managed/manages/

_________________
Chris Bredesen
Senior Software Maintenance Engineer, JBoss


Last edited by cbredesen on Wed May 28, 2008 5:28 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Wed May 28, 2008 5:15 pm 
Newbie

Joined: Wed May 28, 2008 10:46 am
Posts: 4
I couldn't get thread-managed session contexts to work with HibernateTransactionManager.
Besides, session has to be stored in HttpSession because the same object should be availible through many ajax requests. And isn't each request run in a separete thread?

_________________
Jakub Wiśniewski


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 28, 2008 5:27 pm 
Red Hat Associate
Red Hat Associate

Joined: Mon Aug 16, 2004 11:14 am
Posts: 253
Location: Raleigh, NC
AFIAK thread context and Hibernate transactions should work. But that's another discussion.

Read about long conversations and session-per-conversation here:

http://www.hibernate.org/hib_docs/v3/re ... tions.html

Then understand that Seam is designed from the ground up to do exactly what you want with exactly the components you want in a very efficient manner. Before you embroil yourself in trying to solve this problem, I would take a good hard look at Seam (which if you don't know was created by Gavin King, the creator of Hibernate).

And yes, you would need to span multiple requests for AJAX service but I don't think HTTPSession is the right place for your long running conversation, though some have quite possibly made it work. The important part is to not hold the database connection open during the think time because you will torpedo scalability right from the get go.

_________________
Chris Bredesen
Senior Software Maintenance Engineer, JBoss


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 28, 2008 6:09 pm 
Newbie

Joined: Wed May 28, 2008 10:46 am
Posts: 4
Thanks for reply.

I'll think of using seam in my next project, when i have more time to get to know this framework.
The combination of spring, hibernate and jsf works fine for me. I is realy lightweight, so development goes fast and easy.

Question about your last sentece. Why open database connection will torpedo scalability? I undestand that keeping transaction open can cause such effect, but what open database connection has to do with it?

_________________
Jakub Wiśniewski


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 29, 2008 11:09 am 
Red Hat Associate
Red Hat Associate

Joined: Mon Aug 16, 2004 11:14 am
Posts: 253
Location: Raleigh, NC
Let me back up from my prior statement about HTTPSession and replace this with "make sure you know what you're doing" if you choose this route. See here:

http://www.hibernate.org/43.html#A5

Holding open connections for long durations is generally bad practice. Acquire late, release early is the only way to scale concurrent users against limited db connections.

_________________
Chris Bredesen
Senior Software Maintenance Engineer, JBoss


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 29, 2008 12:18 pm 
Newbie

Joined: Wed May 28, 2008 10:46 am
Posts: 4
Ok, thanks for your sugestions.

In case of closing connections there's a way to do this with session.close() before storing session and session.connect(Connection) after retrieving it from HttpSession. This way no database connection should be held, but we get the adventage of sessions cache.

I've seen the article you've attached. And I realy don't think that it would work to store session in thread managed way, bacouse it wouldn't be accessible in defferent requests. The only alternative i can think of is to store it in a bean with w session soupe. But the result will be the same. Wouldn't it?

_________________
Jakub Wiśniewski


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 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.