OpenSessionInViewInterceptor.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 10k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 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. package org.springframework.orm.hibernate.support;
  17. import javax.servlet.http.HttpServletRequest;
  18. import javax.servlet.http.HttpServletResponse;
  19. import net.sf.hibernate.FlushMode;
  20. import net.sf.hibernate.HibernateException;
  21. import net.sf.hibernate.Session;
  22. import org.springframework.dao.DataAccessException;
  23. import org.springframework.orm.hibernate.HibernateAccessor;
  24. import org.springframework.orm.hibernate.SessionFactoryUtils;
  25. import org.springframework.orm.hibernate.SessionHolder;
  26. import org.springframework.transaction.support.TransactionSynchronizationManager;
  27. import org.springframework.web.servlet.HandlerInterceptor;
  28. import org.springframework.web.servlet.ModelAndView;
  29. /**
  30.  * Spring web HandlerInterceptor that binds a Hibernate Session to the thread for the
  31.  * entire processing of the request. Intended for the "Open Session in View" pattern,
  32.  * i.e. to allow for lazy loading in web views despite the original transactions
  33.  * already being completed.
  34.  *
  35.  * <p>This interceptor works similar to the AOP HibernateInterceptor: It just makes
  36.  * Hibernate Sessions available via the thread. It is suitable for non-transactional
  37.  * execution but also for middle tier transactions via HibernateTransactionManager
  38.  * or JtaTransactionManager. In the latter case, Sessions pre-bound by this interceptor
  39.  * will automatically be used for the transactions and flushed accordingly.
  40.  *
  41.  * <p>In contrast to OpenSessionInViewFilter, this interceptor is set up in a Spring
  42.  * application context and can thus take advantage of bean wiring. It derives from
  43.  * HibernateAccessor to inherit common Hibernate configuration properties.
  44.  *
  45.  * <p><b>WARNING:</b> Applying this interceptor to existing logic can cause issues that
  46.  * have not appeared before, through the use of a single Hibernate Session for the
  47.  * processing of an entire request. In particular, the reassociation of persistent
  48.  * objects with a Hibernate Session has to occur at the very beginning of request
  49.  * processing, to avoid clashes will already loaded instances of the same objects.
  50.  *
  51.  * <p>Alternatively, turn this interceptor into deferred close mode, by specifying
  52.  * "singleSession"="false": It will not use a single session per request then,
  53.  * but rather let each data access operation respectively transaction use its own
  54.  * session (like without Open Session in View). Each of those sessions will be
  55.  * registered for deferred close, though, actually processed at request completion.
  56.  *
  57.  * <p>A single session per request allows for most efficient first-level caching,
  58.  * but can cause side effects, for example on saveOrUpdate or if continuing
  59.  * after a rolled-back transaction. The deferred close strategy is as safe as
  60.  * no Open Session in View in that respect, while still allowing for lazy loading
  61.  * in views (but not providing a first-level cache for the entire request).
  62.  *
  63.  * <p><b>NOTE</b>: This interceptor will by default not flush the Hibernate session,
  64.  * as it assumes to be used in combination with middle tier transactions that care for
  65.  * the flushing, or HibernateAccessors with flushMode FLUSH_EAGER. If you want this
  66.  * interceptor to flush after the handler has been invoked but before view rendering,
  67.  * set the flushMode of this interceptor to FLUSH_AUTO in such a scenario. Note that
  68.  * the flushMode of this interceptor will just apply in single session mode!
  69.  *
  70.  * @author Juergen Hoeller
  71.  * @since 06.12.2003
  72.  * @see #setSingleSession
  73.  * @see #setFlushMode
  74.  * @see OpenSessionInViewFilter
  75.  * @see org.springframework.orm.hibernate.HibernateInterceptor
  76.  * @see org.springframework.orm.hibernate.HibernateTransactionManager
  77.  * @see org.springframework.orm.hibernate.SessionFactoryUtils#getSession
  78.  * @see org.springframework.transaction.support.TransactionSynchronizationManager
  79.  */
  80. public class OpenSessionInViewInterceptor extends HibernateAccessor implements HandlerInterceptor {
  81. /**
  82.  * Suffix that gets appended to the SessionFactory toString representation
  83.  * for the "participate in existing session handling" request attribute.
  84.  * @see #getParticipateAttributeName
  85.  */
  86. public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
  87. private boolean singleSession = true;
  88. /**
  89.  * Create a new OpenSessionInViewInterceptor,
  90.  * turning the default flushMode to FLUSH_NEVER.
  91.  * @see #setFlushMode
  92.  */
  93. public OpenSessionInViewInterceptor() {
  94. setFlushMode(FLUSH_NEVER);
  95. }
  96. /**
  97.  * Set whether to use a single session for each request. Default is true.
  98.  * <p>If set to false, each data access operation respectively transaction
  99.  * will use its own session (like without Open Session in View). Each of
  100.  * those sessions will be registered for deferred close, though, actually
  101.  * processed at request completion.
  102.  * @see SessionFactoryUtils#initDeferredClose
  103.  * @see SessionFactoryUtils#processDeferredClose
  104.  */
  105. public void setSingleSession(boolean singleSession) {
  106. this.singleSession = singleSession;
  107. }
  108. /**
  109.  * Return whether to use a single session for each request.
  110.  */
  111. protected boolean isSingleSession() {
  112. return singleSession;
  113. }
  114. /**
  115.  * Open a new Hibernate Session according to the settings of this HibernateAccessor
  116.  * and binds in to the thread via TransactionSynchronizationManager.
  117.  * @see org.springframework.orm.hibernate.SessionFactoryUtils#getSession
  118.  * @see org.springframework.transaction.support.TransactionSynchronizationManager
  119.  */
  120. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  121.     throws DataAccessException {
  122. if ((isSingleSession() && TransactionSynchronizationManager.hasResource(getSessionFactory())) ||
  123.     SessionFactoryUtils.isDeferredCloseActive(getSessionFactory())) {
  124. // do not modify the Session: just mark the request accordingly
  125. String participateAttributeName = getParticipateAttributeName();
  126. Integer count = (Integer) request.getAttribute(participateAttributeName);
  127. int newCount = (count != null) ? count.intValue() + 1 : 1;
  128. request.setAttribute(getParticipateAttributeName(), new Integer(newCount));
  129. }
  130. else {
  131. if (isSingleSession()) {
  132. // single session mode
  133. logger.debug("Opening single Hibernate session in OpenSessionInViewInterceptor");
  134. Session session = SessionFactoryUtils.getSession(
  135. getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
  136. if (getFlushMode() == FLUSH_NEVER) {
  137. session.setFlushMode(FlushMode.NEVER);
  138. }
  139. TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
  140. }
  141. else {
  142. // deferred close mode
  143. SessionFactoryUtils.initDeferredClose(getSessionFactory());
  144. }
  145. }
  146. return true;
  147. }
  148. /**
  149.  * Flush the Hibernate Session before view rendering, if necessary.
  150.  * Note that this just applies in single session mode!
  151.  * <p>The default is FLUSH_NEVER to avoid this extra flushing, assuming that
  152.  * middle tier transactions have flushed their changes on commit.
  153.  * @see #setFlushMode
  154.  */
  155. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  156.     ModelAndView modelAndView) throws DataAccessException {
  157. if (isSingleSession()) {
  158. // only potentially flush in single session mode
  159. SessionHolder sessionHolder =
  160. (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
  161. logger.debug("Flushing single Hibernate session in OpenSessionInViewInterceptor");
  162. try {
  163. flushIfNecessary(sessionHolder.getSession(), false);
  164. }
  165. catch (HibernateException ex) {
  166. throw convertHibernateAccessException(ex);
  167. }
  168. }
  169. }
  170. /**
  171.  * Unbind the Hibernate Session from the thread and closes it (in single session mode),
  172.  * respectively process deferred close for all sessions that have been opened during
  173.  * the current request (in deferred close mode).
  174.  * @see org.springframework.orm.hibernate.SessionFactoryUtils#closeSessionIfNecessary
  175.  * @see org.springframework.transaction.support.TransactionSynchronizationManager
  176.  */
  177. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  178.     Exception ex) throws DataAccessException {
  179. String participateAttributeName = getParticipateAttributeName();
  180. Integer count = (Integer) request.getAttribute(participateAttributeName);
  181. if (count != null) {
  182. // do not modify the Session: just clear the marker
  183. if (count.intValue() > 1) {
  184. request.setAttribute(participateAttributeName, new Integer(count.intValue() - 1));
  185. }
  186. else {
  187. request.removeAttribute(participateAttributeName);
  188. }
  189. }
  190. else {
  191. if (isSingleSession()) {
  192. // single session mode
  193. SessionHolder sessionHolder =
  194. (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
  195. logger.debug("Closing single Hibernate session in OpenSessionInViewInterceptor");
  196. SessionFactoryUtils.closeSessionIfNecessary(sessionHolder.getSession(), getSessionFactory());
  197. }
  198. else {
  199. // deferred close mode
  200. SessionFactoryUtils.processDeferredClose(getSessionFactory());
  201. }
  202. }
  203. }
  204. /**
  205.  * Return the name of the request attribute that identifies that a request is
  206.  * already filtered. Default implementation takes the toString representation
  207.  * of the SessionFactory instance and appends ".FILTERED".
  208.  * @see #PARTICIPATE_SUFFIX
  209.  */
  210. protected String getParticipateAttributeName() {
  211. return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
  212. }
  213. }