KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > hibernate3 > support > OpenSessionInViewFilter


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.orm.hibernate3.support;
18
19 import java.io.IOException JavaDoc;
20
21 import javax.servlet.FilterChain JavaDoc;
22 import javax.servlet.ServletException JavaDoc;
23 import javax.servlet.http.HttpServletRequest JavaDoc;
24 import javax.servlet.http.HttpServletResponse JavaDoc;
25
26 import org.hibernate.FlushMode;
27 import org.hibernate.Session;
28 import org.hibernate.SessionFactory;
29
30 import org.springframework.dao.DataAccessResourceFailureException;
31 import org.springframework.orm.hibernate3.SessionFactoryUtils;
32 import org.springframework.orm.hibernate3.SessionHolder;
33 import org.springframework.transaction.support.TransactionSynchronizationManager;
34 import org.springframework.web.context.WebApplicationContext;
35 import org.springframework.web.context.support.WebApplicationContextUtils;
36 import org.springframework.web.filter.OncePerRequestFilter;
37
38 /**
39  * Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire
40  * processing of the request. Intended for the "Open Session in View" pattern,
41  * i.e. to allow for lazy loading in web views despite the original transactions
42  * already being completed.
43  *
44  * <p>This filter works similar to the AOP HibernateInterceptor: It just makes
45  * Hibernate Sessions available via the thread. It is suitable for non-transactional
46  * execution but also for business layer transactions via HibernateTransactionManager
47  * or JtaTransactionManager. In the latter case, Sessions pre-bound by this filter
48  * will automatically be used for the transactions and flushed accordingly.
49  *
50  * <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
51  * have not appeared before, through the use of a single Hibernate Session for the
52  * processing of an entire request. In particular, the reassociation of persistent
53  * objects with a Hibernate Session has to occur at the very beginning of request
54  * processing, to avoid clashes with already loaded instances of the same objects.
55  *
56  * <p>Alternatively, turn this filter into deferred close mode, by specifying
57  * "singleSession"="false": It will not use a single session per request then,
58  * but rather let each data access operation or transaction use its own session
59  * (like without Open Session in View). Each of those sessions will be registered
60  * for deferred close, though, actually processed at request completion.
61  *
62  * <p>A single session per request allows for most efficient first-level caching,
63  * but can cause side effects, for example on saveOrUpdate or if continuing
64  * after a rolled-back transaction. The deferred close strategy is as safe as
65  * no Open Session in View in that respect, while still allowing for lazy loading
66  * in views (but not providing a first-level cache for the entire request).
67  *
68  * <p>Looks up the SessionFactory in Spring's root web application context.
69  * Supports a "sessionFactoryBeanName" filter init-param in <code>web.xml</code>;
70  * the default bean name is "sessionFactory". Looks up the SessionFactory on each
71  * request, to avoid initialization order issues (when using ContextLoaderServlet,
72  * the root application context will get initialized <i>after</i> this filter).
73  *
74  * <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
75  * as it assumes to be used in combination with service layer transactions that care
76  * for the flushing, or HibernateAccessors with flushMode FLUSH_EAGER. If you want this
77  * filter to flush after completed request processing, override <code>closeSession</code>
78  * and invoke <code>flush</code> on the Session before closing it. Additionally, you will
79  * also need to override <code>getSession()</code> to return a Session in a flush mode
80  * other than the default <code>FlushMode.NEVER</code>. Note that <code>getSession</code>
81  * and <code>closeSession</code> will just be invoked in single session mode!
82  *
83  * @author Juergen Hoeller
84  * @since 1.2
85  * @see #setSingleSession
86  * @see #closeSession
87  * @see #lookupSessionFactory
88  * @see OpenSessionInViewInterceptor
89  * @see org.springframework.orm.hibernate3.HibernateInterceptor
90  * @see org.springframework.orm.hibernate3.HibernateTransactionManager
91  * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
92  * @see org.springframework.transaction.support.TransactionSynchronizationManager
93  */

94 public class OpenSessionInViewFilter extends OncePerRequestFilter {
95
96     public static final String JavaDoc DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
97
98
99     private String JavaDoc sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
100
101     private boolean singleSession = true;
102
103     private FlushMode flushMode = FlushMode.NEVER;
104
105
106     /**
107      * Set the bean name of the SessionFactory to fetch from Spring's
108      * root application context. Default is "sessionFactory".
109      * @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
110      */

111     public void setSessionFactoryBeanName(String JavaDoc sessionFactoryBeanName) {
112         this.sessionFactoryBeanName = sessionFactoryBeanName;
113     }
114
115     /**
116      * Return the bean name of the SessionFactory to fetch from Spring's
117      * root application context.
118      */

119     protected String JavaDoc getSessionFactoryBeanName() {
120         return this.sessionFactoryBeanName;
121     }
122
123     /**
124      * Set whether to use a single session for each request. Default is "true".
125      * <p>If set to "false", each data access operation or transaction will use
126      * its own session (like without Open Session in View). Each of those
127      * sessions will be registered for deferred close, though, actually
128      * processed at request completion.
129      * @see SessionFactoryUtils#initDeferredClose
130      * @see SessionFactoryUtils#processDeferredClose
131      */

132     public void setSingleSession(boolean singleSession) {
133         this.singleSession = singleSession;
134     }
135
136     /**
137      * Return whether to use a single session for each request.
138      */

139     protected boolean isSingleSession() {
140         return this.singleSession;
141     }
142
143     public void setFlushMode(FlushMode flushMode) {
144         this.flushMode = flushMode;
145     }
146
147     protected FlushMode getFlushMode() {
148         return this.flushMode;
149     }
150
151
152     protected void doFilterInternal(
153             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, FilterChain JavaDoc filterChain)
154             throws ServletException JavaDoc, IOException JavaDoc {
155
156         SessionFactory sessionFactory = lookupSessionFactory(request);
157         boolean participate = false;
158
159         if (isSingleSession()) {
160             // single session mode
161
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
162                 // Do not modify the Session: just set the participate flag.
163
participate = true;
164             }
165             else {
166                 logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
167                 Session session = getSession(sessionFactory);
168                 TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
169             }
170         }
171         else {
172             // deferred close mode
173
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
174                 // Do not modify deferred close: just set the participate flag.
175
participate = true;
176             }
177             else {
178                 SessionFactoryUtils.initDeferredClose(sessionFactory);
179             }
180         }
181
182         try {
183             filterChain.doFilter(request, response);
184         }
185
186         finally {
187             if (!participate) {
188                 if (isSingleSession()) {
189                     // single session mode
190
SessionHolder sessionHolder =
191                             (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
192                     logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
193                     closeSession(sessionHolder.getSession(), sessionFactory);
194                 }
195                 else {
196                     // deferred close mode
197
SessionFactoryUtils.processDeferredClose(sessionFactory);
198                 }
199             }
200         }
201     }
202
203     /**
204      * Look up the SessionFactory that this filter should use,
205      * taking the current HTTP request as argument.
206      * <p>Default implementation delegates to the <code>lookupSessionFactory</code>
207      * without arguments.
208      * @return the SessionFactory to use
209      * @see #lookupSessionFactory()
210      */

211     protected SessionFactory lookupSessionFactory(HttpServletRequest JavaDoc request) {
212         return lookupSessionFactory();
213     }
214
215     /**
216      * Look up the SessionFactory that this filter should use.
217      * <p>Default implementation looks for a bean with the specified name
218      * in Spring's root application context.
219      * @return the SessionFactory to use
220      * @see #getSessionFactoryBeanName
221      */

222     protected SessionFactory lookupSessionFactory() {
223         if (logger.isDebugEnabled()) {
224             logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
225         }
226         WebApplicationContext wac =
227                 WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
228         return (SessionFactory) wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
229     }
230
231     /**
232      * Get a Session for the SessionFactory that this filter uses.
233      * Note that this just applies in single session mode!
234      * <p>The default implementation delegates to the
235      * <code>SessionFactoryUtils.getSession</code> method and
236      * sets the <code>Session</code>'s flush mode to "NEVER".
237      * <p>Can be overridden in subclasses for creating a Session with a
238      * custom entity interceptor or JDBC exception translator.
239      * @param sessionFactory the SessionFactory that this filter uses
240      * @return the Session to use
241      * @throws DataAccessResourceFailureException if the Session could not be created
242      * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
243      * @see org.hibernate.FlushMode#NEVER
244      */

245     protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
246         Session session = SessionFactoryUtils.getSession(sessionFactory, true);
247         FlushMode flushMode = getFlushMode();
248         if (flushMode != null) {
249             session.setFlushMode(flushMode);
250         }
251         return session;
252     }
253
254     /**
255      * Close the given Session.
256      * Note that this just applies in single session mode!
257      * <p>Can be overridden in subclasses, e.g. for flushing the Session before
258      * closing it. See class-level javadoc for a discussion of flush handling.
259      * Note that you should also override getSession accordingly, to set
260      * the flush mode to something else than NEVER.
261      * @param session the Session used for filtering
262      * @param sessionFactory the SessionFactory that this filter uses
263      */

264     protected void closeSession(Session session, SessionFactory sessionFactory) {
265         SessionFactoryUtils.closeSession(session);
266     }
267
268 }
269
Popular Tags