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

更多学习交流,请加群(非诚勿扰):
这里写图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值