OpenSessionInView 的作用、配置及原理

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

把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现”Open Session in View”的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。
OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中,它将自动被 spring 的事务管理器探测到。所以 OpenSessionInViewFilter 适用于 Service 层使用HibernateTransactionManager 或 JtaTransactionManager 进行事务管理的环境,也可以用于非事务只读的数据操作中。

在不同的技术框架下,实现Open session in view的手段不同:
在servlet中用过滤器实现
在struts中用***实现
在spring中使用AOP实现
这里给出在ssh框架中用spring的AOP实现:

配置:

<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还未关闭,这便是懒加载了。

更多学习交流,请加群(非诚勿扰):

全部评论

相关推荐

宇智波爱学习:我还没收到笔试
投递荣耀等公司10个岗位
点赞 评论 收藏
分享
11-01 20:03
已编辑
门头沟学院 算法工程师
Amazarashi66:这种也是幸存者偏差了,拿不到这个价的才是大多数
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务