web.xml中openSessionInViewFilter的作用

Hibernate 允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常

(eg: org.hibernate.LazyInitializationException:(LazyInitializationException.java:42)

  • failed to lazily initialize a collection of role: cn.easyjava.bean.product.ProductType.childtypes, no session or session was closed
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.easyjava.bean.product.ProductType.childtypes, no session or session was closed)。

把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。

OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中,它将自动被 Spring 的事务管理器探测到。所以 OpenSessionInViewFilter 适用于 Service 层使用HibernateTransactionManager 或 JtaTransactionManager 进行事务管理的环境,也可以用于非事务只读的数据操作中。

配置:

<filter>
        <filter-name>Spring OpenSessionInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
     <init-param>
   
<!--
指定org.springframework.orm.hibernate3.LocalSessionFactoryBean在spring配置文件中的名称,默认值为sessionFactory
     如果LocalSessionFactoryBean在spring中的名称不是sessionFactory,该参数一定要指定,否则会出现找不到sessionFactory的例外
-->
     <param-name>sessionFactoryBean</param-name>
   <param-value>sessionFactory</param-value>
  </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Spring OpenSessionInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

原理:

    protected void doFilterInternal(  
                HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
                throws ServletException, IOException {  
            SessionFactory sessionFactory = lookupSessionFactory(request);  
            boolean participate = false;  
            if (isSingleSession()) {  
                // single session mode  
                if (TransactionSynchronizationManager.hasResource(sessionFactory)) {  
                    // Do not modify the Session: just set the participate flag.  
                    participate = true;  
                }  
                else {  
                    logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");  
                    Session session = getSession(sessionFactory);  
                    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));  
                }  
            }  
            else {  
                // deferred close mode  
                if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {  
                    // Do not modify deferred close: just set the participate flag.  
                    participate = true;  
                }  
                else {  
                    SessionFactoryUtils.initDeferredClose(sessionFactory);  
                }  
            }  
            try {  
                filterChain.doFilter(request, response);  
            }  
            finally {  
                if (!participate) {  
                    if (isSingleSession()) {  
                        // single session mode  
                        SessionHolder sessionHolder =  
                                (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);  
                        logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");  
                        closeSession(sessionHolder.getSession(), sessionFactory);  
                    }  
                    else {  
                        // deferred close mode  
                        SessionFactoryUtils.processDeferredClose(sessionFactory);  
                    }  
                }  
            }  
        }  
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        /**
         * 从spring的上下文中取得SessionFactory对象
         * WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
         * return (SessionFactory) wac.getBean(getSessionFactoryBeanName(),SessionFactory.class);
         * getSessionFactoryBeanName()方法默认返回"sessionFactory"字符串,在spring配置文件中可要注意了,别写错了.
         */
        SessionFactory sessionFactory = lookupSessionFactory(request);
        boolean participate = false;// 标识过滤器结束时是否进行关闭session等后续处理
        if (isSingleSession()) {//单session模式
            //判断能否在当前线程中取得sessionFactory对象对应的session
            if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
                //当能够找到session的时候证明会有相关类处理session的收尾工作,这个过滤器不能进行关闭session操作,否则会出现重复关闭的情况.
                participate = true;//但我并没有想出正常使用的情况下什么时候会出现这种情况.
            } else {
                Session session = getSession(sessionFactory);//当前线程取不到session的时候通过sessionFactory创建,下面还会详细介绍此方法
                //将session绑定到当前的线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(new HashMap(1));
                //但是单session模式下一个SessionHolder对应一个session,核心方法是getValidatedSession 取得一个open状态下的session,并且取出后从map中移出.
                TransactionSynchronizationManager.bindResource(sessionFactory,
 
;                new SessionHolder(session));
        }
    } else {//这段是非单session模式的处理情况,没有研究.但粗略看上去,大概思想是一样的
        if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
            participate = true;
        } else {
            SessionFactoryUtils.initDeferredClose(sessionFactory);
        }
    }
    try {
        //将session绑定到了当前线程后,就该处理请求了
        filterChain.doFilter(request, response);
    }finally {
        if (!participate) {
            if (isSingleSession()) {
                //当请求结束时,对于单session模式,这时候要取消session的绑定,因为web容器(Tomcat等)的线程是采用线程池机制的,线程使用过后并不会销毁.
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
                        .unbindResource(sessionFactory);
                //取消绑定只是取消session对象和线程对象之间的引用,还没有关闭session,不关闭session相对于不关闭数据库连接,所以这里要关闭session
                closeSession(sessionHolder.getSession(), sessionFactory);
            } else {
                //非单session模式,没有研究.
                SessionFactoryUtils.processDeferredClose(sessionFactory);
            }
        }
    }
} 

步骤:

  1. 获取session,打开session

  2. filterChain.doFilter(request, response);

  3. 关闭session

在2中可能执行了若干的Servlet、JSP、Action等等,最终处理完渲染完页面之后,再进入OpenSessionInViewFilter的3关闭session
现在只要保证在2中不论是Servlet、JSP还是Action中执行DAO时获取的session都是1中打开的同一个session,并且在DAO关闭session时并不实际关闭,留到OpenSessionInViewFilter的3中再最终关闭,就实现了懒加载了,因为只要是在OpenSessionInViewFilter过滤的范围内,session都处于打开,比如在一个Servlet中查到一个Bean,这时他的关联实体并没有加载,当这个Servlet重定向到一个JSP,在其中得到这个Bean后直接访问之前没加载的那些关联实体,会实时的加载这个关联实体,因为session还未关闭,这便是懒加载了。


转载:https://blog.csdn.net/ggibenben1314/article/details/46289411

猜你喜欢

转载自blog.csdn.net/qq_44752641/article/details/106304277
今日推荐