Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » hibernate3 » support » [javadoc | source]
    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;
   20   
   21   import javax.servlet.FilterChain;
   22   import javax.servlet.ServletException;
   23   import javax.servlet.http.HttpServletRequest;
   24   import javax.servlet.http.HttpServletResponse;
   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 makes Hibernate Sessions available via the current thread, which
   45    * will be autodetected by transaction managers. It is suitable for service layer
   46    * transactions via {@link org.springframework.orm.hibernate3.HibernateTransactionManager}
   47    * or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
   48    * as for non-transactional execution (if configured appropriately).
   49    *
   50    * <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
   51    * with the flush mode set to <code>FlushMode.NEVER</code>. It assumes to be used
   52    * in combination with service layer transactions that care for the flushing: The
   53    * active transaction manager will temporarily change the flush mode to
   54    * <code>FlushMode.AUTO</code> during a read-write transaction, with the flush
   55    * mode reset to <code>FlushMode.NEVER</code> at the end of each transaction.
   56    * If you intend to use this filter without transactions, consider changing
   57    * the default flush mode (through the "flushMode" property).
   58    *
   59    * <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
   60    * have not appeared before, through the use of a single Hibernate Session for the
   61    * processing of an entire request. In particular, the reassociation of persistent
   62    * objects with a Hibernate Session has to occur at the very beginning of request
   63    * processing, to avoid clashes with already loaded instances of the same objects.
   64    *
   65    * <p>Alternatively, turn this filter into deferred close mode, by specifying
   66    * "singleSession"="false": It will not use a single session per request then,
   67    * but rather let each data access operation or transaction use its own session
   68    * (like without Open Session in View). Each of those sessions will be registered
   69    * for deferred close, though, actually processed at request completion.
   70    *
   71    * <p>A single session per request allows for most efficient first-level caching,
   72    * but can cause side effects, for example on <code>saveOrUpdate</code> or when
   73    * continuing after a rolled-back transaction. The deferred close strategy is as safe
   74    * as no Open Session in View in that respect, while still allowing for lazy loading
   75    * in views (but not providing a first-level cache for the entire request).
   76    *
   77    * <p>Looks up the SessionFactory in Spring's root web application context.
   78    * Supports a "sessionFactoryBeanName" filter init-param in <code>web.xml</code>;
   79    * the default bean name is "sessionFactory". Looks up the SessionFactory on each
   80    * request, to avoid initialization order issues (when using ContextLoaderServlet,
   81    * the root application context will get initialized <i>after</i> this filter).
   82    *
   83    * @author Juergen Hoeller
   84    * @since 1.2
   85    * @see #setSingleSession
   86    * @see #setFlushMode
   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 DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
   97   
   98   
   99   	private String 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 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 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   	/**
  144   	 * Specify the Hibernate FlushMode to apply to this filter's
  145   	 * {@link org.hibernate.Session}. Only applied in single session mode.
  146   	 * <p>Can be populated with the corresponding constant name in XML bean
  147   	 * definitions: e.g. "AUTO".
  148   	 * <p>The default is "NEVER". Specify "AUTO" if you intend to use
  149   	 * this filter without service layer transactions.
  150   	 * @see org.hibernate.Session#setFlushMode
  151   	 * @see org.hibernate.FlushMode#NEVER
  152   	 * @see org.hibernate.FlushMode#AUTO
  153   	 */
  154   	public void setFlushMode(FlushMode flushMode) {
  155   		this.flushMode = flushMode;
  156   	}
  157   
  158   	/**
  159   	 * Return the Hibernate FlushMode that this filter applies to its
  160   	 * {@link org.hibernate.Session} (in single session mode).
  161   	 */
  162   	protected FlushMode getFlushMode() {
  163   		return this.flushMode;
  164   	}
  165   
  166   
  167   	protected void doFilterInternal(
  168   			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  169   			throws ServletException, IOException {
  170   
  171   		SessionFactory sessionFactory = lookupSessionFactory(request);
  172   		boolean participate = false;
  173   
  174   		if (isSingleSession()) {
  175   			// single session mode
  176   			if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
  177   				// Do not modify the Session: just set the participate flag.
  178   				participate = true;
  179   			}
  180   			else {
  181   				logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
  182   				Session session = getSession(sessionFactory);
  183   				TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
  184   			}
  185   		}
  186   		else {
  187   			// deferred close mode
  188   			if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
  189   				// Do not modify deferred close: just set the participate flag.
  190   				participate = true;
  191   			}
  192   			else {
  193   				SessionFactoryUtils.initDeferredClose(sessionFactory);
  194   			}
  195   		}
  196   
  197   		try {
  198   			filterChain.doFilter(request, response);
  199   		}
  200   
  201   		finally {
  202   			if (!participate) {
  203   				if (isSingleSession()) {
  204   					// single session mode
  205   					SessionHolder sessionHolder =
  206   							(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
  207   					logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
  208   					closeSession(sessionHolder.getSession(), sessionFactory);
  209   				}
  210   				else {
  211   					// deferred close mode
  212   					SessionFactoryUtils.processDeferredClose(sessionFactory);
  213   				}
  214   			}
  215   		}
  216   	}
  217   
  218   	/**
  219   	 * Look up the SessionFactory that this filter should use,
  220   	 * taking the current HTTP request as argument.
  221   	 * <p>The default implementation delegates to the {@link #lookupSessionFactory()}
  222   	 * variant without arguments.
  223   	 * @param request the current request
  224   	 * @return the SessionFactory to use
  225   	 */
  226   	protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
  227   		return lookupSessionFactory();
  228   	}
  229   
  230   	/**
  231   	 * Look up the SessionFactory that this filter should use.
  232   	 * <p>The default implementation looks for a bean with the specified name
  233   	 * in Spring's root application context.
  234   	 * @return the SessionFactory to use
  235   	 * @see #getSessionFactoryBeanName
  236   	 */
  237   	protected SessionFactory lookupSessionFactory() {
  238   		if (logger.isDebugEnabled()) {
  239   			logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
  240   		}
  241   		WebApplicationContext wac =
  242   				WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
  243   		return (SessionFactory) wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
  244   	}
  245   
  246   	/**
  247   	 * Get a Session for the SessionFactory that this filter uses.
  248   	 * Note that this just applies in single session mode!
  249   	 * <p>The default implementation delegates to the
  250   	 * <code>SessionFactoryUtils.getSession</code> method and
  251   	 * sets the <code>Session</code>'s flush mode to "NEVER".
  252   	 * <p>Can be overridden in subclasses for creating a Session with a
  253   	 * custom entity interceptor or JDBC exception translator.
  254   	 * @param sessionFactory the SessionFactory that this filter uses
  255   	 * @return the Session to use
  256   	 * @throws DataAccessResourceFailureException if the Session could not be created
  257   	 * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
  258   	 * @see org.hibernate.FlushMode#NEVER
  259   	 */
  260   	protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
  261   		Session session = SessionFactoryUtils.getSession(sessionFactory, true);
  262   		FlushMode flushMode = getFlushMode();
  263   		if (flushMode != null) {
  264   			session.setFlushMode(flushMode);
  265   		}
  266   		return session;
  267   	}
  268   
  269   	/**
  270   	 * Close the given Session.
  271   	 * Note that this just applies in single session mode!
  272   	 * <p>Can be overridden in subclasses, e.g. for flushing the Session before
  273   	 * closing it. See class-level javadoc for a discussion of flush handling.
  274   	 * Note that you should also override getSession accordingly, to set
  275   	 * the flush mode to something else than NEVER.
  276   	 * @param session the Session used for filtering
  277   	 * @param sessionFactory the SessionFactory that this filter uses
  278   	 */
  279   	protected void closeSession(Session session, SessionFactory sessionFactory) {
  280   		SessionFactoryUtils.closeSession(session);
  281   	}
  282   
  283   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » hibernate3 » support » [javadoc | source]