【问题标题】:OpenSessionInViewFilter and Wicket and still running into LazyInitializationExceptionsOpenSessionInViewFilter 和 Wicket 仍然遇到 LazyInitializationExceptions
【发布时间】:2013-04-01 12:44:59
【问题描述】:

我将 Spring 的 OpenSessionInViewFilter 与 Wicket 结合使用,但我一直遇到 LazyInitializationExceptions,我无法解决。我可以在日志中看到休眠会话在引发异常之前已关闭,所以我显然做错了。

【问题讨论】:

    标签: spring hibernate wicket


    【解决方案1】:

    OSIVFilter 必须在 WicketFilter 之上,否则请求将无法到达它。 WicketFilter 自己处理页面请求,它不委托给其他组件,例如另一个 servlet(因此,过滤器链停在那里)。

    另一种方法是创建与 OSIVFilter 相同的IRequestCycleListener 实现。我通过修改 Spring 的过滤器代码以符合新接口创建了这样的类,如下所示:

    import org.apache.wicket.MetaDataKey;
    import org.apache.wicket.protocol.http.WebApplication;
    import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
    import org.apache.wicket.request.cycle.RequestCycle;
    import org.hibernate.FlushMode;
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.dao.DataAccessResourceFailureException;
    import org.springframework.orm.hibernate4.SessionFactoryUtils;
    import org.springframework.orm.hibernate4.SessionHolder;
    import org.springframework.transaction.support.TransactionSynchronizationManager;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    
    public class OpenSessionInViewRequestCycleListener extends AbstractRequestCycleListener {
    
        protected final Logger logger = LoggerFactory.getLogger(getClass());
    
        public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
    
        private static final MetaDataKey<Boolean> PARTICIPATE = new MetaDataKey<Boolean>() {
        };
    
        private static final MetaDataKey<SessionFactory> SESSION_FACTORY = new MetaDataKey<SessionFactory>() {
        };
    
        private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
    
        private final WebApplication application;
    
        public OpenSessionInViewRequestCycleListener(WebApplication application) {
            this.application = application;
        }
    
        /**
         * Set the bean name of the SessionFactory to fetch from Spring's
         * root application context. Default is "sessionFactory".
         * @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
         */
        public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
            this.sessionFactoryBeanName = sessionFactoryBeanName;
        }
    
        /**
         * Return the bean name of the SessionFactory to fetch from Spring's
         * root application context.
         */
        protected String getSessionFactoryBeanName() {
            return this.sessionFactoryBeanName;
        }
    
        @Override
        public void onBeginRequest(RequestCycle cycle) {
            SessionFactory sessionFactory = lookupSessionFactory(cycle);
            cycle.setMetaData(SESSION_FACTORY, sessionFactory);
            cycle.setMetaData(PARTICIPATE, false);
    
            if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
                // Do not modify the Session: just set the participate flag.
                cycle.setMetaData(PARTICIPATE, true);
            }
            else {
                logger.debug("Opening Hibernate Session in OpenSessionInViewRequestCycleListener");
                Session session = openSession(sessionFactory);
                SessionHolder sessionHolder = new SessionHolder(session);
                TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
            }
        }
    
        @Override
        public void onEndRequest(RequestCycle cycle) {
            if (!cycle.getMetaData(PARTICIPATE)) {
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(cycle.getMetaData(SESSION_FACTORY));
                logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
                SessionFactoryUtils.closeSession(sessionHolder.getSession());
            }
        }
    
        /**
         * Look up the SessionFactory that this filter should use,
         * taking the current HTTP request as argument.
         * <p>The default implementation delegates to the {@link #lookupSessionFactory()}
         * variant without arguments.
         * @param cycle the current request
         * @return the SessionFactory to use
         */
        protected SessionFactory lookupSessionFactory(RequestCycle cycle) {
            return lookupSessionFactory();
        }
    
        /**
         * Look up the SessionFactory that this filter should use.
         * <p>The default implementation looks for a bean with the specified name
         * in Spring's root application context.
         * @return the SessionFactory to use
         * @see #getSessionFactoryBeanName
         */
        protected SessionFactory lookupSessionFactory() {
            if (logger.isDebugEnabled()) {
                logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
            }
            WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(this.application.getServletContext());
            return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
        }
    
        /**
         * Open a Session for the SessionFactory that this filter uses.
         * <p>The default implementation delegates to the
         * {@code SessionFactory.openSession} method and
         * sets the {@code Session}'s flush mode to "MANUAL".
         * @param sessionFactory the SessionFactory that this filter uses
         * @return the Session to use
         * @throws DataAccessResourceFailureException if the Session could not be created
         * @see org.hibernate.FlushMode#MANUAL
         */
        protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
            try {
                Session session = sessionFactory.openSession();
                session.setFlushMode(FlushMode.MANUAL);
                return session;
            } catch (HibernateException ex) {
                throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
            }
        }
    
    }
    

    【讨论】:

    • 将 OSIV 过滤器放在 servlet 的 web.xml 中的 Wicket 过滤器之上解决了问题。
    • 它有效,但有一个缺点:如果 OSIVFilter 抛出异常,用户将看到 Tomcat 的错误页面,而不是 Wicket 的错误页面。这可能是一个问题,也可能不是。
    猜你喜欢
    • 1970-01-01
    • 2013-08-30
    • 1970-01-01
    • 2020-06-04
    • 2016-08-27
    • 2012-12-28
    • 1970-01-01
    • 2015-12-25
    • 1970-01-01
    相关资源
    最近更新 更多