/*
* Databinder: a simple bridge from Wicket to Hibernate
* Copyright (C) 2006 Nathan Hamblen nathan@technically.us
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.databinder.hib;
import org.apache.wicket.Application;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.WicketRuntimeException;
import org.hibernate.SessionFactory;
import org.hibernate.context.ManagedSessionContext;
/**
* Provides access to application-bound Hibernate session factories and current sessions.
* This class will work with a
* <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/context/ManagedSessionContext.html">ManagedSessionContext</a>
* and DataRequestCycle listener when present, but neither is required so long as a
* "current" session is available from the session factory supplied by the application.
* @see HibernateApplication
* @author Nathan Hamblen
*/
public class Databinder {
/**
* @return default session factory, as returned by the application
* @throws WicketRuntimeException if session factory can not be found
* @see HibernateApplication
*/
public static SessionFactory getHibernateSessionFactory() {
return getHibernateSessionFactory(null);
}
/**
* @param key object, or null for the default factory
* @return session factory, as returned by the application
* @throws WicketRuntimeException if session factory can not be found
* @see HibernateApplication
*/
public static SessionFactory getHibernateSessionFactory(Object key) {
Application app = Application.get();
if (app instanceof HibernateApplication)
return ((HibernateApplication)app).getHibernateSessionFactory(key);
throw new WicketRuntimeException("Please implement HibernateApplication in your Application subclass.");
}
/**
* @return default Hibernate session bound to current thread
*/
public static org.hibernate.classic.Session getHibernateSession() {
return getHibernateSession(null);
}
/**
* @param key or null for the default factory
* @return Hibernate session bound to current thread
*/
public static org.hibernate.classic.Session getHibernateSession(Object key) {
dataSessionRequested(key);
return getHibernateSessionFactory(key).getCurrentSession();
}
/**
* @return true if a session is bound for the default factory
*/
public static boolean hasBoundSession() {
return hasBoundSession(null);
}
/**
* @param key or null for the default factory
* @return true if a session is bound for the keyed factory
*/
public static boolean hasBoundSession(Object key) {
return ManagedSessionContext.hasBind(getHibernateSessionFactory(key));
}
/**
* Notifies current request cycle that a data session was requested, if a session factory
* was not already bound for this thread and the request cycle is an DataRequestCycle.
* @param key or null for the default factory
* @see HibernateRequestCycle
*/
private static void dataSessionRequested(Object key) {
if (!hasBoundSession(key)) {
// if session is unavailable, it could be a late-loaded conversational cycle
RequestCycle cycle = RequestCycle.get();
if (cycle instanceof HibernateRequestCycle)
((HibernateRequestCycle)cycle).dataSessionRequested(key);
}
}
/**
* Wraps SessionUnit callback in a temporary thread-bound Hibernate session from the default
* factory if necessary. This is to be used outside of a regular a session-handling request cycle,
* such as during application init or an external Web service request.
* The temporary session and transaction, if created, are closed after the callback returns and
* uncommited transactions are rolled back. Be careful of returning detached Hibernate
* objects that may not be fully loaded with data; consider using projections / scalar
* queries instead.<b>Note</b> This method uses a ManagedSessionContext. With JTA
* or other forms of current session lookup a wrapping session will not be
* detected and a new one will always be created.
* @param unit work to be performed in thread-bound session
* @see SessionUnit
*/
public static Object ensureSession(SessionUnit unit) {
return ensureSession(unit, null);
}
/**
* Wraps SessionUnit callback in a temporary thread-bound Hibernate session from the keyed
* factory if necessary. This is to be used outside of a regular a session-handling request cycle,
* such as during application init or an external Web service request.
* The temporary session and transaction, if created, are closed after the callback returns and
* uncommited transactions are rolled back. Be careful of returning detached Hibernate
* objects that may not be fully loaded with data; consider using projections / scalar
* queries instead. <b>Note</b> This method uses a ManagedSessionContext. With JTA
* or other forms of current session lookup a wrapping session will not be
* detected and a new one will always be created.
* @param unit work to be performed in thread-bound session
* @param key or null for the default factory
* @see SessionUnit
*/
public static Object ensureSession(SessionUnit unit, Object key) {
dataSessionRequested(key);
SessionFactory sf = getHibernateSessionFactory(key);
if (ManagedSessionContext.hasBind(sf))
return unit.run(getHibernateSession(key));
org.hibernate.classic.Session sess = sf.openSession();
try {
sess.beginTransaction();
ManagedSessionContext.bind(sess);
return unit.run(sess);
} finally {
try {
if (sess.getTransaction().isActive())
sess.getTransaction().rollback();
} finally {
sess.close();
ManagedSessionContext.unbind(sf);
}
}
}
}