KickJava   Java API By Example, From Geeks To Geeks.

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


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.hibernate3.support;
18
19 import org.hibernate.HibernateException;
20 import org.hibernate.Session;
21
22 import org.springframework.dao.DataAccessException;
23 import org.springframework.orm.hibernate3.HibernateAccessor;
24 import org.springframework.orm.hibernate3.SessionFactoryUtils;
25 import org.springframework.orm.hibernate3.SessionHolder;
26 import org.springframework.transaction.support.TransactionSynchronizationManager;
27 import org.springframework.ui.ModelMap;
28 import org.springframework.web.context.request.WebRequest;
29 import org.springframework.web.context.request.WebRequestInterceptor;
30
31 /**
32  * Spring web HandlerInterceptor that binds a Hibernate Session to the thread for the
33  * entire processing of the request. Intended for the "Open Session in View" pattern,
34  * that is, to allow for lazy loading in web views despite the original transactions
35  * already being completed.
36  *
37  * <p>This interceptor works similar to the AOP HibernateInterceptor: It just makes
38  * Hibernate Sessions available via the thread. It is suitable for non-transactional
39  * execution but also for service layer transactions via HibernateTransactionManager
40  * or JtaTransactionManager. In the latter case, Sessions pre-bound by this interceptor
41  * will automatically be used for the transactions and flushed accordingly.
42  *
43  * <p>In contrast to OpenSessionInViewFilter, this interceptor is set up in a Spring
44  * application context and can thus take advantage of bean wiring. It derives from
45  * HibernateAccessor to inherit common Hibernate configuration properties.
46  *
47  * <p><b>WARNING:</b> Applying this interceptor to existing logic can cause issues that
48  * have not appeared before, through the use of a single Hibernate Session for the
49  * processing of an entire request. In particular, the reassociation of persistent
50  * objects with a Hibernate Session has to occur at the very beginning of request
51  * processing, to avoid clashes will already loaded instances of the same objects.
52  *
53  * <p>Alternatively, turn this interceptor into deferred close mode, by specifying
54  * "singleSession"="false": It will not use a single session per request then,
55  * but rather let each data access operation or transaction use its own session
56  * (like without Open Session in View). Each of those sessions will be registered
57  * for deferred close, though, actually processed at request completion.
58  *
59  * <p>A single session per request allows for most efficient first-level caching,
60  * but can cause side effects, for example on saveOrUpdate or if continuing
61  * after a rolled-back transaction. The deferred close strategy is as safe as
62  * no Open Session in View in that respect, while still allowing for lazy loading
63  * in views (but not providing a first-level cache for the entire request).
64  *
65  * <p><b>NOTE</b>: This interceptor will by default <i>not</i> flush the Hibernate Session,
66  * as it assumes to be used in combination with service layer transactions that care
67  * for the flushing, or HibernateAccessors with "flushMode" FLUSH_EAGER. If you want this
68  * interceptor to flush after the handler has been invoked but before view rendering,
69  * set the "flushMode" of this interceptor to FLUSH_AUTO in such a scenario. Note that
70  * the "flushMode" of this interceptor will just apply in single session mode!
71  *
72  * @author Juergen Hoeller
73  * @since 1.2
74  * @see #setSingleSession
75  * @see #setFlushMode
76  * @see OpenSessionInViewFilter
77  * @see org.springframework.orm.hibernate3.HibernateInterceptor
78  * @see org.springframework.orm.hibernate3.HibernateTransactionManager
79  * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
80  * @see org.springframework.transaction.support.TransactionSynchronizationManager
81  */

82 public class OpenSessionInViewInterceptor extends HibernateAccessor implements WebRequestInterceptor {
83
84     /**
85      * Suffix that gets appended to the SessionFactory toString representation
86      * for the "participate in existing session handling" request attribute.
87      * @see #getParticipateAttributeName
88      */

89     public static final String JavaDoc PARTICIPATE_SUFFIX = ".PARTICIPATE";
90
91
92     private boolean singleSession = true;
93
94
95     /**
96      * Create a new OpenSessionInViewInterceptor,
97      * turning the default flushMode to FLUSH_NEVER.
98      * @see #setFlushMode
99      */

100     public OpenSessionInViewInterceptor() {
101         setFlushMode(FLUSH_NEVER);
102     }
103
104     /**
105      * Set whether to use a single session for each request. Default is "true".
106      * <p>If set to false, each data access operation or transaction will use
107      * its own session (like without Open Session in View). Each of those
108      * sessions will be registered for deferred close, though, actually
109      * processed at request completion.
110      * @see SessionFactoryUtils#initDeferredClose
111      * @see SessionFactoryUtils#processDeferredClose
112      */

113     public void setSingleSession(boolean singleSession) {
114         this.singleSession = singleSession;
115     }
116
117     /**
118      * Return whether to use a single session for each request.
119      */

120     protected boolean isSingleSession() {
121         return singleSession;
122     }
123
124
125     /**
126      * Open a new Hibernate Session according to the settings of this HibernateAccessor
127      * and binds in to the thread via TransactionSynchronizationManager.
128      * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
129      * @see org.springframework.transaction.support.TransactionSynchronizationManager
130      */

131     public void preHandle(WebRequest request) throws DataAccessException {
132         if ((isSingleSession() && TransactionSynchronizationManager.hasResource(getSessionFactory())) ||
133             SessionFactoryUtils.isDeferredCloseActive(getSessionFactory())) {
134             // Do not modify the Session: just mark the request accordingly.
135
String JavaDoc participateAttributeName = getParticipateAttributeName();
136             Integer JavaDoc count = (Integer JavaDoc) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
137             int newCount = (count != null) ? count.intValue() + 1 : 1;
138             request.setAttribute(getParticipateAttributeName(), new Integer JavaDoc(newCount), WebRequest.SCOPE_REQUEST);
139         }
140         else {
141             if (isSingleSession()) {
142                 // single session mode
143
logger.debug("Opening single Hibernate Session in OpenSessionInViewInterceptor");
144                 Session session = SessionFactoryUtils.getSession(
145                         getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
146                 applyFlushMode(session, false);
147                 TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
148             }
149             else {
150                 // deferred close mode
151
SessionFactoryUtils.initDeferredClose(getSessionFactory());
152             }
153         }
154     }
155
156     /**
157      * Flush the Hibernate Session before view rendering, if necessary.
158      * Note that this just applies in single session mode!
159      * <p>The default is FLUSH_NEVER to avoid this extra flushing, assuming that
160      * service layer transactions have flushed their changes on commit.
161      * @see #setFlushMode
162      */

163     public void postHandle(WebRequest request, ModelMap model) throws DataAccessException {
164         if (isSingleSession()) {
165             // Only potentially flush in single session mode.
166
SessionHolder sessionHolder =
167                     (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
168             logger.debug("Flushing single Hibernate Session in OpenSessionInViewInterceptor");
169             try {
170                 flushIfNecessary(sessionHolder.getSession(), false);
171             }
172             catch (HibernateException ex) {
173                 throw convertHibernateAccessException(ex);
174             }
175         }
176     }
177
178     /**
179      * Unbind the Hibernate Session from the thread and closes it (in single session
180      * mode), or process deferred close for all sessions that have been opened
181      * during the current request (in deferred close mode).
182      * @see org.springframework.transaction.support.TransactionSynchronizationManager
183      */

184     public void afterCompletion(WebRequest request, Exception JavaDoc ex) throws DataAccessException {
185         String JavaDoc participateAttributeName = getParticipateAttributeName();
186         Integer JavaDoc count = (Integer JavaDoc) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
187         if (count != null) {
188             // Do not modify the Session: just clear the marker.
189
if (count.intValue() > 1) {
190                 request.setAttribute(participateAttributeName, new Integer JavaDoc(count.intValue() - 1), WebRequest.SCOPE_REQUEST);
191             }
192             else {
193                 request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
194             }
195         }
196         else {
197             if (isSingleSession()) {
198                 // single session mode
199
SessionHolder sessionHolder =
200                         (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
201                 logger.debug("Closing single Hibernate Session in OpenSessionInViewInterceptor");
202                 SessionFactoryUtils.closeSession(sessionHolder.getSession());
203             }
204             else {
205                 // deferred close mode
206
SessionFactoryUtils.processDeferredClose(getSessionFactory());
207             }
208         }
209     }
210
211     /**
212      * Return the name of the request attribute that identifies that a request is
213      * already filtered. Default implementation takes the toString representation
214      * of the SessionFactory instance and appends ".PARTICIPATE".
215      * @see #PARTICIPATE_SUFFIX
216      */

217     protected String JavaDoc getParticipateAttributeName() {
218         return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
219     }
220
221 }
222
Popular Tags