KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 2002-2006 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.hibernate.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 net.sf.hibernate.FlushMode;
27 import net.sf.hibernate.Session;
28 import net.sf.hibernate.SessionFactory;
29
30 import org.springframework.dao.DataAccessResourceFailureException;
31 import org.springframework.orm.hibernate.SessionFactoryUtils;
32 import org.springframework.orm.hibernate.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 06.12.2003
85  * @see #setSingleSession
86  * @see #closeSession
87  * @see #lookupSessionFactory
88  * @see OpenSessionInViewInterceptor
89  * @see org.springframework.orm.hibernate.HibernateInterceptor
90  * @see org.springframework.orm.hibernate.HibernateTransactionManager
91  * @see org.springframework.orm.hibernate.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
104     /**
105      * Set the bean name of the SessionFactory to fetch from Spring's
106      * root application context. Default is "sessionFactory".
107      * @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
108      */

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

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

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

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

201     protected SessionFactory lookupSessionFactory(HttpServletRequest JavaDoc request) {
202         return lookupSessionFactory();
203     }
204
205     /**
206      * Look up the SessionFactory that this filter should use.
207      * <p>Default implementation looks for a bean with the specified name
208      * in Spring's root application context.
209      * @return the SessionFactory to use
210      * @see #getSessionFactoryBeanName
211      */

212     protected SessionFactory lookupSessionFactory() {
213         if (logger.isDebugEnabled()) {
214             logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
215         }
216         WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
217         return (SessionFactory) wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
218     }
219
220     /**
221      * Get a Session for the SessionFactory that this filter uses.
222      * Note that this just applies in single session mode!
223      * <p>The default implementation delegates to SessionFactoryUtils'
224      * getSession method and sets the Session's flushMode to NEVER.
225      * <p>Can be overridden in subclasses for creating a Session with a custom
226      * entity interceptor or JDBC exception translator.
227      * @param sessionFactory the SessionFactory that this filter uses
228      * @return the Session to use
229      * @throws DataAccessResourceFailureException if the Session could not be created
230      * @see org.springframework.orm.hibernate.SessionFactoryUtils#getSession(SessionFactory, boolean)
231      * @see net.sf.hibernate.FlushMode#NEVER
232      */

233     protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
234         return openSession(sessionFactory);
235     }
236
237     /**
238      * Get a Session for the SessionFactory that this filter uses.
239      * Note that this just applies in single session mode!
240      * <p>The default implementation delegates to the
241      * <code>SessionFactoryUtils.getSession</code> method and
242      * sets the <code>Session</code>'s flush mode to "NEVER".
243      * <p>Can be overridden in subclasses for creating a Session with a
244      * custom entity interceptor or JDBC exception translator.
245      * @param sessionFactory the SessionFactory that this filter uses
246      * @return the Session to use
247      * @throws DataAccessResourceFailureException if the Session could not be created
248      * @see org.springframework.orm.hibernate.SessionFactoryUtils#getSession(SessionFactory, boolean)
249      * @see net.sf.hibernate.FlushMode#NEVER
250      */

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

267     protected void closeSession(Session session, SessionFactory sessionFactory) {
268         SessionFactoryUtils.closeSession(session);
269     }
270
271 }
272
Popular Tags