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 java.io.IOException;
  18. import javax.servlet.FilterChain;
  19. import javax.servlet.ServletException;
  20. import javax.servlet.http.HttpServletRequest;
  21. import javax.servlet.http.HttpServletResponse;
  22. import net.sf.hibernate.FlushMode;
  23. import net.sf.hibernate.Session;
  24. import net.sf.hibernate.SessionFactory;
  25. import org.springframework.dao.CleanupFailureDataAccessException;
  26. import org.springframework.dao.DataAccessResourceFailureException;
  27. import org.springframework.orm.hibernate.SessionFactoryUtils;
  28. import org.springframework.orm.hibernate.SessionHolder;
  29. import org.springframework.transaction.support.TransactionSynchronizationManager;
  30. import org.springframework.web.context.WebApplicationContext;
  31. import org.springframework.web.context.support.WebApplicationContextUtils;
  32. import org.springframework.web.filter.OncePerRequestFilter;
  33. /**
  34. * Servlet 2.3 Filter that binds a Hibernate Session to the thread for the whole
  35. * processing of the request. Intended for the "Open Session in View" pattern,
  36. * i.e. to allow for lazy loading in web views despite the original transactions
  37. * already being completed.
  38. *
  39. * <p>This filter works similar to the AOP HibernateInterceptor: It just makes
  40. * Hibernate Sessions available via the thread. It is suitable for non-transactional
  41. * execution but also for middle tier transactions via HibernateTransactionManager
  42. * or JtaTransactionManager. In the latter case, Sessions pre-bound by this filter
  43. * will automatically be used for the transactions and flushed accordingly.
  44. *
  45. * <p><b>WARNING:</b> Applying this filter 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>Looks up the SessionFactory in Spring's root web application context.
  52. * Supports a "sessionFactoryBeanName" filter init-param; the default bean name is
  53. * "sessionFactory". Looks up the SessionFactory on each request, to avoid
  54. * initialization order issues (if using ContextLoaderServlet, the root
  55. * application context will get initialized <i>after</i> this filter).
  56. *
  57. * <p><b>NOTE</b>: This filter will by default not flush the Hibernate session, as
  58. * it assumes to be used in combination with middle tier transactions that care for
  59. * the flushing, or HibernateAccessors with flushMode FLUSH_EAGER. If you want this
  60. * filter to flush after completed request processing, override closeSession and
  61. * invoke flush on the Session before closing it.
  62. *
  63. * @author Juergen Hoeller
  64. * @since 06.12.2003
  65. * @see OpenSessionInViewInterceptor
  66. * @see #closeSession
  67. * @see org.springframework.orm.hibernate.HibernateInterceptor
  68. * @see org.springframework.orm.hibernate.HibernateTransactionManager
  69. * @see org.springframework.orm.hibernate.SessionFactoryUtils#getSession
  70. * @see org.springframework.transaction.support.TransactionSynchronizationManager
  71. */
  72. public class OpenSessionInViewFilter extends OncePerRequestFilter {
  73. public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
  74. private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
  75. /**
  76. * Set the bean name of the SessionFactory to fetch from Spring's
  77. * root application context.
  78. */
  79. public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
  80. this.sessionFactoryBeanName = sessionFactoryBeanName;
  81. }
  82. /**
  83. * Return the bean name of the SessionFactory to fetch from Spring's
  84. * root application context.
  85. */
  86. protected String getSessionFactoryBeanName() {
  87. return sessionFactoryBeanName;
  88. }
  89. /**
  90. * This implementation appends the SessionFactory bean name to the class name,
  91. * to be executed one per SessionFactory. Can be overridden in subclasses,
  92. * e.g. when also overriding lookupSessionFactory.
  93. * @see #lookupSessionFactory
  94. */
  95. protected String getAlreadyFilteredAttributeName() {
  96. return getClass().getName() + "." + this.sessionFactoryBeanName;
  97. }
  98. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
  99. FilterChain filterChain) throws ServletException, IOException {
  100. SessionFactory sessionFactory = lookupSessionFactory();
  101. logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
  102. Session session = getSession(sessionFactory);
  103. TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
  104. try {
  105. filterChain.doFilter(request, response);
  106. }
  107. finally {
  108. TransactionSynchronizationManager.unbindResource(sessionFactory);
  109. logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
  110. closeSession(session, sessionFactory);
  111. }
  112. }
  113. /**
  114. * Look up the SessionFactory that this filter should use.
  115. * The default implementation looks for a bean with the specified name
  116. * in Spring's root application context.
  117. * @return the SessionFactory to use
  118. * @see #getSessionFactoryBeanName
  119. */
  120. protected SessionFactory lookupSessionFactory() {
  121. logger.info("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
  122. WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
  123. return (SessionFactory) wac.getBean(getSessionFactoryBeanName());
  124. }
  125. /**
  126. * Get a Session for the SessionFactory that this filter uses.
  127. * The default implementation invokes SessionFactoryUtils.getSession,
  128. * and sets the Session's flushMode to NEVER.
  129. * <p>Can be overridden in subclasses for creating a Session with a custom
  130. * entity interceptor or JDBC exception translator.
  131. * @param sessionFactory the SessionFactory that this filter uses
  132. * @return the Session to use
  133. * @throws DataAccessResourceFailureException if the Session could not be created
  134. * @see org.springframework.orm.hibernate.SessionFactoryUtils#getSession(SessionFactory, boolean)
  135. */
  136. protected Session getSession(SessionFactory sessionFactory)
  137. throws DataAccessResourceFailureException {
  138. Session session = SessionFactoryUtils.getSession(sessionFactory, true);
  139. session.setFlushMode(FlushMode.NEVER);
  140. return session;
  141. }
  142. /**
  143. * Close the given Session.
  144. * The default implementation invokes SessionFactoryUtils.closeSessionIfNecessary.
  145. * <p>Can be overridden in subclasses, e.g. for flushing the Session before
  146. * closing it. See class-level javadoc for a discussion of flush handling.
  147. * @param session the Session used for filtering
  148. * @param sessionFactory the SessionFactory that this filter uses
  149. * @throws DataAccessResourceFailureException if the Session could not be closed
  150. */
  151. protected void closeSession(Session session, SessionFactory sessionFactory)
  152. throws CleanupFailureDataAccessException {
  153. SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
  154. }
  155. }