001 /*
002 * Databinder: a simple bridge from Wicket to Hibernate
003 * Copyright (C) 2006 Nathan Hamblen nathan@technically.us
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
018 */
019
020 package net.databinder.hib;
021
022
023
024 import org.apache.wicket.Application;
025 import org.apache.wicket.RequestCycle;
026 import org.apache.wicket.WicketRuntimeException;
027 import org.hibernate.SessionFactory;
028 import org.hibernate.context.ManagedSessionContext;
029
030 /**
031 * Provides access to application-bound Hibernate session factories and current sessions.
032 * This class will work with a
033 * <a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/context/ManagedSessionContext.html">ManagedSessionContext</a>
034 * and DataRequestCycle listener when present, but neither is required so long as a
035 * "current" session is available from the session factory supplied by the application.
036 * @see HibernateApplication
037 * @author Nathan Hamblen
038 */
039 public class Databinder {
040
041 /**
042 * @return default session factory, as returned by the application
043 * @throws WicketRuntimeException if session factory can not be found
044 * @see HibernateApplication
045 */
046 public static SessionFactory getHibernateSessionFactory() {
047 return getHibernateSessionFactory(null);
048 }
049 /**
050 * @param key object, or null for the default factory
051 * @return session factory, as returned by the application
052 * @throws WicketRuntimeException if session factory can not be found
053 * @see HibernateApplication
054 */
055 public static SessionFactory getHibernateSessionFactory(Object key) {
056 Application app = Application.get();
057 if (app instanceof HibernateApplication)
058 return ((HibernateApplication)app).getHibernateSessionFactory(key);
059 throw new WicketRuntimeException("Please implement HibernateApplication in your Application subclass.");
060 }
061
062 /**
063 * @return default Hibernate session bound to current thread
064 */
065 public static org.hibernate.classic.Session getHibernateSession() {
066 return getHibernateSession(null);
067 }
068 /**
069 * @param key or null for the default factory
070 * @return Hibernate session bound to current thread
071 */
072 public static org.hibernate.classic.Session getHibernateSession(Object key) {
073 dataSessionRequested(key);
074 return getHibernateSessionFactory(key).getCurrentSession();
075 }
076 /**
077 * @return true if a session is bound for the default factory
078 */
079 public static boolean hasBoundSession() {
080 return hasBoundSession(null);
081 }
082
083 /**
084 * @param key or null for the default factory
085 * @return true if a session is bound for the keyed factory
086 */
087 public static boolean hasBoundSession(Object key) {
088 return ManagedSessionContext.hasBind(getHibernateSessionFactory(key));
089 }
090
091 /**
092 * Notifies current request cycle that a data session was requested, if a session factory
093 * was not already bound for this thread and the request cycle is an DataRequestCycle.
094 * @param key or null for the default factory
095 * @see HibernateRequestCycle
096 */
097 private static void dataSessionRequested(Object key) {
098 if (!hasBoundSession(key)) {
099 // if session is unavailable, it could be a late-loaded conversational cycle
100 RequestCycle cycle = RequestCycle.get();
101 if (cycle instanceof HibernateRequestCycle)
102 ((HibernateRequestCycle)cycle).dataSessionRequested(key);
103 }
104 }
105
106 /**
107 * Wraps SessionUnit callback in a temporary thread-bound Hibernate session from the default
108 * factory if necessary. This is to be used outside of a regular a session-handling request cycle,
109 * such as during application init or an external Web service request.
110 * The temporary session and transaction, if created, are closed after the callback returns and
111 * uncommited transactions are rolled back. Be careful of returning detached Hibernate
112 * objects that may not be fully loaded with data; consider using projections / scalar
113 * queries instead.<b>Note</b> This method uses a ManagedSessionContext. With JTA
114 * or other forms of current session lookup a wrapping session will not be
115 * detected and a new one will always be created.
116 * @param unit work to be performed in thread-bound session
117 * @see SessionUnit
118 */
119 public static Object ensureSession(SessionUnit unit) {
120 return ensureSession(unit, null);
121 }
122 /**
123 * Wraps SessionUnit callback in a temporary thread-bound Hibernate session from the keyed
124 * factory if necessary. This is to be used outside of a regular a session-handling request cycle,
125 * such as during application init or an external Web service request.
126 * The temporary session and transaction, if created, are closed after the callback returns and
127 * uncommited transactions are rolled back. Be careful of returning detached Hibernate
128 * objects that may not be fully loaded with data; consider using projections / scalar
129 * queries instead. <b>Note</b> This method uses a ManagedSessionContext. With JTA
130 * or other forms of current session lookup a wrapping session will not be
131 * detected and a new one will always be created.
132 * @param unit work to be performed in thread-bound session
133 * @param key or null for the default factory
134 * @see SessionUnit
135 */
136 public static Object ensureSession(SessionUnit unit, Object key) {
137 dataSessionRequested(key);
138 SessionFactory sf = getHibernateSessionFactory(key);
139 if (ManagedSessionContext.hasBind(sf))
140 return unit.run(getHibernateSession(key));
141 org.hibernate.classic.Session sess = sf.openSession();
142 try {
143 sess.beginTransaction();
144 ManagedSessionContext.bind(sess);
145 return unit.run(sess);
146 } finally {
147 try {
148 if (sess.getTransaction().isActive())
149 sess.getTransaction().rollback();
150 } finally {
151 sess.close();
152 ManagedSessionContext.unbind(sf);
153 }
154 }
155 }
156 }