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...)