用户操作
[即时聊天] [发私信] [加为好友]
cacID:cacling
35820次访问,排名3260(1)好友0人,关注者0
cacling的文章
原创 1 篇
翻译 1 篇
转载 1 篇
评论 2 篇
cac的公告
SpringSide Community
最近评论
文章分类
收藏
    相册
    友情链接
    InfoQ
    robbin的口水
    SpringSide
    花钱的年华
    透明思考
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 Open Session In View 探讨收藏

    新一篇: Selenium备忘手册 | 

        在没有使用Spring提供的Open Session In View情况下,因需要在service(or Dao)层里把session关闭,所以lazy loading true的话,要在应用层内把关系集合都初始化,如 company.getEmployees(),否则Hibernatesession already closed Exception;    Open Session In View提供了一种简便的方法,较好地解决了lazy loading问题.

        它有两种配置方式OpenSessionInViewInterceptorOpenSessionInViewFilter(具体参看SpringSide),功能相同,只是一个在web.xml配置,另一个在application.xml配置而已。

        Open Session In Viewrequestsession绑定到当前thread期间一直保持hibernate sessionopen状态,使sessionrequest的整个期间都可以使用,如在View层里PO也可以lazy loading数据,如 ${ company.employees }。当View 层逻辑完成后,才会通过FilterdoFilter方法或InterceptorpostHandle方法自动关闭session 

    OpenSessionInViewInterceptor配置

    <beans>
       
    <bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
             
    <property name="sessionFactory">
             
    <ref bean="sessionFactory"/>
        
    </property>
       
    </bean>
       
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
           
    <property name="interceptors">
             
    <list>
                
    <ref bean="openSessionInViewInterceptor"/>
           
    </list>
         
    </property>
         
    <property name="mappings">
         ... 
        
    </property>
       
    </bean> ...
    </beans> 

    OpenSessionInViewFilter配置

    <web-app>
     ... 
      
    <filter>
         
    <filter-name>hibernateFilter</filter-name>
         
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class>
         
    <!-- singleSession默认为true,若设为false则等于没用OpenSessionInView -->
        
    <init-param>
           
    <param-name>singleSession</param-name>
           
    <param-value>true</param-value>
        
    </init-param>
      
    </filter>
     ... 
      
    <filter-mapping>
         
    <filter-name>hibernateFilter</filter-name>
         
    <url-pattern>*.do</url-pattern>
       
    </filter-mapping>
     ... 
    </web-app> 

    很多人在使用OpenSessionInView过程中提及一个错误:

    org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition 

    看看OpenSessionInViewFilter里的opensession方法

    protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
        Session session 
    = SessionFactoryUtils.getSession(sessionFactory, true);
        session.setFlushMode(FlushMode.NEVER);
        
    return session;
    }

        可以看到OpenSessionInViewFilter在getSession的时候,会把获取回来的session的flush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到TransactionSynchronizationManager,使request的整个过程都使用同一个session,在请求过后再接除该sessionFactory的绑定,最后closeSessionIfNecessary根据该session是否已和transaction绑定来决定是否关闭session。在这个过程中,若HibernateTemplate 发现自当前session有不是readOnly的transaction,就会获取到FlushMode.AUTO Session,使方法拥有写权限。

        也即是,如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有insert,update,delete操作权限,如果没有transaction,并且没有另外人为地设flush model的话,则doFilter的整个过程都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有。

    采用spring的事务声明,使方法受transaction控制

        <bean id="baseTransaction" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"       abstract="true">
             
    <property name="transactionManager" ref="transactionManager"/>
             
    <property name="proxyTargetClass" value="true"/>
             
    <property name="transactionAttributes">
                 
    <props>
                     
    <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                     
    <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                     
    <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
                     
    <prop key="save*">PROPAGATION_REQUIRED</prop>
                     
    <prop key="add*">PROPAGATION_REQUIRED</prop>
                     
    <prop key="update*">PROPAGATION_REQUIRED</prop>
                     
    <prop key="remove*">PROPAGATION_REQUIRED</prop>
                 
    </props>
             
    </property>
         
    </bean>

         
    <bean id="userService" parent="baseTransaction">
             
    <property name="target">
                 
    <bean class="com.phopesoft.security.service.impl.UserServiceImpl"/>
             
    </property>
        
    </bean> 

    对于上例,则以save,add,update,remove开头的方法拥有可写的事务,如果当前有某个方法,如命名为importExcel(),则因没有transaction而没有写权限,这时若方法内有insert,update,delete操作的话,则需要手动设置flush model为Flush.AUTO,如

    session.setFlushMode(FlushMode.AUTO); 
    session.save(user);  
    session.flush(); 

         尽管Open Session In View看起来还不错,其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternal方法代码,这个方法实际上是被父类的doFilter调用的,因此,我们可以大约了解的OpenSessionInViewFilter调用流程: request(请求)->open session并开始transaction->controller->View(Jsp)->结束transaction并close session.

         一切看起来很正确,尤其是在本地开发测试的时候没出现问题,但试想下如果流程中的某一步被阻塞的话,那在这期间connection就一直被占用而不释放。最有可能被阻塞的就是在写Jsp这步,一方面可能是页面内容大,response.write的时间长,另一方面可能是网速慢,服务器与用户间传输时间久。当大量这样的情况出现时,就有连接池连接不足,造成页面假死现象。

    Open Session In View是个双刃剑,放在公网上内容多流量大的网站请慎用。

     

    发表于 @ 2006年11月29日 18:07:00|评论(loading...)|编辑

    新一篇: Selenium备忘手册 | 

    评论:没有评论。

    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © cac