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