`

一个很纠结的问题。OpenSessionInViewFilter

阅读更多
当hibernate+spring配合使用的时候,如果设置了lazy=true,那么在读取数据的时候,当读取了父数据后,hibernate会自动关闭session,这样,当要使用子数据的时候,系统会抛出lazyinit的错误,这时就需要使用spring提供的 OpenSessionInViewFilter,

OpenSessionInViewFilter主要是保持Session状态知道request将全部页面发送到客户端,这样就可以解决延迟加载带来的问题

3、说说Webwork中使用OpenSessionInView的注意事项

web.xml中的配置要注意先后顺序,OpenSessionInViewFilter要在Webwork的filter前面,否则系统会报错。

代码



1.<filter>  
2.        <filter-name>opensession</filter-name>  
3.        <filter-class>  
4.              org.springframework.orm.hibernate3.support.OpenSessionInViewFilter   
5.        </filter-class>  
6.        <init-param>  
7.            <param-name>singleSession</param-name>  
8.            <param-value>true</param-value>  
9.        </init-param>  
10.        <init-param>  
11.            <param-name>sessionFactoryBeanName</param-name>  
12.            <param-value>mySessionFactory</param-value>    
13.        </init-param>  
14.    </filter>  
15.        
16.    <filter>  
17.        <filter-name>webwork</filter-name>  
18.        <filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class>  
19.    </filter>  
20.         <filter-mapping>  
21.      <filter-name>opensession</filter-name>  
22.      <url-pattern>/*</url-pattern>    
23.    </filter-mapping>  
24.    <filter-mapping>  
25.        <filter-name>webwork</filter-name>  
26.        <url-pattern>/*</url-pattern>  
27.    </filter-mapping>  


对于OpenSessionInView的配置中,singleSession应该设置为true,表示一个request只能打开一个 session,如果设置为false的话,session可以被打开多个,这时在update、delete的时候会出现打开多个session的异常。

但是当设置为true的时候,系统的性能会因为用户的网络状况受到影响,当request在生成页面完成后,session才会被释放,所以如果用户的网络状况比较差,那么连接池中的链接会迟迟不被回收,造成内存增加,系统性能受损。但是如果不用这种方法的话,lazy模式有体现不出它的优点,用?不用?两难啊

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

    OpenSessionInView在request把session绑定到当前thread期间一直保持hibernate session在open状态,使session在request的整个期间都可以使用,如在View层里PO也可以lazy loading数据,如 ${ company.employees }。当View 层逻辑完成后,才会通过Filter的doFilter方法或Interceptor的postHandle方法自动关闭session。


OpenSessionInViewInterceptor配置


1.
<beans>


2.
<bean


3.
class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">


4.
<property >


5.
<ref bean="sessionFactory"/>


6.
</property>


7.
</bean>


8.
<bean


9.
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">


10.
<property >


11.
<list>


12.
<ref bean="openSessionInViewInterceptor"/>


13.
</list>


14.
</property>


15.
<property >


16.
...


17.
</property>


18.
</bean>


19.
...


20.
</beans>




OpenSessionInViewFilter配置


1.
<web-app>


2.
...


3.
<filter>


4.
<filter-name>hibernateFilter</filter-name>


5.
<filter-class>


6.
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter


7.
</filter-class>


8.
<!-- singleSession默认为true,若设为false则等于没用OpenSessionInView -->


9.
<init-param>


10.
<param-name>singleSession</param-name>


11.
<param-value>true</param-value>


12.
</init-param>


13.
</filter>


14.
...


15.
<filter-mapping>


16.
<filter-name>hibernateFilter</filter-name>


17.
<url-pattern>*.do</url-pattern>


18.
</filter-mapping>


19.
...


20.
</web-app>



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


1.
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations


2.
are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into


3.
FlushMode.AUTO or remove 'readOnly' marker from transaction definition



看看OpenSessionInViewFilter里的几个方法


1.
protected void doFilterInternal(HttpServletRequest request,

HttpServletResponse response,FilterChain filterChain)

throws ServletException, IOException {

 SessionFactory sessionFactory = lookupSessionFactory();

 logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");

 Session session = getSession(sessionFactory);

 TransactionSynchronizationManager.bindResource(

  sessionFactory, new SessionHolder(session));

 try {

  filterChain.doFilter(request, response);

 }

 finally {

 TransactionSynchronizationManager.unbindResource(sessionFactory);

 logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");

 closeSession(session, sessionFactory);

 }

}





2.
protected Session getSession(SessionFactory sessionFactory)

throws DataAccessResourceFailureException {

 Session session = SessionFactoryUtils.getSession(sessionFactory, true);

 session.setFlushMode(FlushMode.NEVER);

 return session;

}


3.
protected void closeSession(Session session, SessionFactory sessionFactory)

throws CleanupFailureDataAccessException {

 SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);

}


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


1.
public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory)


2.
throws CleanupFailureDataAccessException {


3.
if (session == null ||

TransactionSynchronizationManager.hasResource(sessionFactory)) {


4.
return;


5.
}


6.
logger.debug("Closing Hibernate session");


7.
try {


8.
session.close();


9.
}


10.
catch (JDBCException ex) {


11.
// SQLException underneath


12.
throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException());


13.
}


14.
catch (HibernateException ex) {


15.
throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex);


16.
}


17.
}



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

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


1.
  <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>


2.    <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,如


1.
session.setFlushMode(FlushMode.AUTO);


2.
session.save(user);


3.
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是个双刃剑,放在公网上内容多流量大的网站请慎用。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

由于OpenSessionInViewFilter把session绑在当前线程上,导致session的生命周期比事务要长,这期间所有事务性操作都在复用这同一个session,由此产生了一些“怪问题”:

1.出现如下错误:

     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 在把session绑在当前线程上的时候,会把session的flush mode 设为FlushMode.NEVER,因此,如果某个方法没有事务或者有只读事务,则不能对session做insert,update,delete操作,除非事先把session的flush mode手动设为auto

方案:

1、将singleSession设为false,这样只要改 web.xml,缺点是Hibernate Session的Instance可能会大增,使用的JDBC Connection量也会大增,如果Connection Pool的maxPoolSize设得太小,很容易就出问题。<!-- singleSession默认为true,若设为false则等于没用OpenSessionInView -->

2、在控制器中自行管理Session的FlushMode,麻烦的是每个有Modify的Method都要多几行程式

  session.setFlushMode(FlushMode.AUTO);

  session.update(user);

  session.flush();

3、Extend OpenSessionInViewFilter,Override protected Session getSession(SessionFactory sessionFactory),将FlushMode直接改为Auto。

4、让方法受Spring的事务控制(service和配置文件对应)


转自:网易-野七的博客。
分享到:
评论

相关推荐

    关于OpenSessionInViewFilter的学习

    OpenSessionInViewFilter是Spring框架中一个非常重要的组件,主要用于解决数据访问层(DAO)与视图层(View)之间的事务管理问题。在Web应用中,由于HTTP请求的无状态性,一次请求通常由多个Servlet过滤器、控制器和...

    OpenSessionInViewFilter

    OpenSessionInViewFilter个人学习总结

    懒加载异常解决.docx

    3. **多线程环境下的问题:** 在多线程环境中,如果对象被多个线程共享,并且在一个线程中关闭了Session,其他线程仍然尝试访问该对象的关联数据时就会出现问题。 #### 三、解决方案——配置...

    Spring hibernate opensessioninview

    `OpenSessionInViewFilter`是Spring为配合Hibernate设计的一个过滤器,其主要作用是在Web请求处理过程中打开并维持一个Hibernate Session,直到视图渲染完成。这样做的好处在于能够确保在整个请求处理过程中Session...

    过滤器对于session周期的控制

    但是,如果设置为`false`,则每次执行数据库操作时都会创建一个新的Session,这可能导致性能问题以及难以管理的Session生命周期。 #### 总结 综上所述,`OpenSessionInViewFilter`是Spring提供的一个强大工具,...

    SSH项目整合示例【OpenSessionInView】所用到的jar包

    SSH是Java Web开发中的一个流行框架组合,由Struts、Hibernate和Spring三个组件构成。这个框架集成提供了模型-视图-控制器(MVC)架构,数据持久化,以及依赖注入和事务管理等功能,大大简化了企业级应用的开发工作...

    spring中lazy=“true”的正常读取关联表(用opensessioninview)

    为了解决这个问题,Spring提供了一个过滤器 `OpenSessionInViewFilter` 或者 `OpenSessionInViewInterceptor`,通常简称为OSIV(Open Session In View)模式。 OSIV模式的核心思想是在Controller层和View层之间保持...

    jar包(struts2.0.8+spring2.0+hibernate3.2)

    **Struts2** 是一个用于构建企业级Web应用程序的开源MVC框架,它基于Action和拦截器的设计模式。Struts2.0.8版本提供了增强的表单处理、国际化、异常处理和模板技术。该框架强调可配置性和灵活性,允许开发者通过XML...

    我的智囊团(SSH)_04_开发提问及回复功能_上

    OpenSessionInViewFilter的主要作用是在HTTP请求的生命周期内保持一个Hibernate的Session,确保在视图层(例如JSP页面)也能访问到数据库会话,解决了“懒加载”问题,避免了因为请求结束而关闭Session导致的数据...

    S2SH集成 案例

    在这个案例中,Hibernate的SessionFactory可能被Spring管理,通过OpenSessionInViewFilter来保证每个HTTP请求都有一个活跃的Hibernate Session,从而解决延迟加载问题。 OpenSessionInViewFilter是一个重要的组件,...

    spring监听器

    1. **OpenSessionInViewFilter**:这是一个由Spring提供的过滤器,用于解决在Web应用程序中使用Hibernate时常见的问题——事务管理。当用户发起一个HTTP请求时,OpenSessionInViewFilter会在请求开始时打开一个新的...

    Sping 事务管理.doc

    OpenSessionInViewFilter解决Web应用程序的问题

    Spring提供的CharacterEncoding和OpenSessionInView功能

    总的来说,`CharacterEncodingFilter`和`OpenSessionInViewFilter`是Spring框架中用于优化Web应用处理的两个关键组件,它们分别解决了字符编码和数据库会话的问题,提高了应用的稳定性和用户体验。同时,了解SSH框架...

    hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法

    为了解决这个问题,我们可以使用 Spring 提供的一个支持类OpenSessionInViewFilter,这个类可以在发起一个页面请求时打开 Hibernate 的 Session,并保持这个 Session 的生命周期,直到这个请求结束。这样可以确保 ...

    错误及解决方案.pdf

    OpenSessionInViewFilter是Spring提供的一个过滤器,它允许在每个HTTP请求开始时打开一个新的Hibernate会话,并且只有在请求结束时(比如在控制器处理完毕后)才关闭会话。这样做的好处是在处理请求的过程中,所有的...

    struts-hibernate-sprint开发详解 乱码问题 连接池

    在Java Web开发中,Struts、Hibernate和Spring的整合是一个常见的技术栈,称为SSH框架。本文将详细解析如何使用SSH框架进行开发,并解决乱码和连接池的问题。 首先,配置数据库是开发的第一步。在MyEclipse中,可以...

    JavaEE技术面试常见问题.doc

    - **单例模式**:确保一个类只有一个实例,并提供一个全局访问点。 - **适配器模式**:将一个接口转换成客户希望的另一个接口。 #### Java基础 1. **面向对象特性** - **封装**:隐藏对象的属性和实现细节。 ...

    SSH整合环境 配置

    - `OpenSessionInViewFilter` 是Spring提供的一个过滤器,用于解决数据访问层(DAO)在Web请求结束时无法关闭Session的问题,防止出现Session过早关闭导致的事务问题。`sessionFactoryBeanName`参数指定了...

    Open_Session_In_View详解.doc

    在使用Hibernate进行对象持久化时,经常遇到的一个问题是关于懒加载(lazy loading)的处理。懒加载是一种优化技术,允许在真正需要某个关联对象的数据时才加载它们,而不是一开始就加载所有相关联的数据。这种策略...

    Struts2+Spring+hibernate中对action的单元测试环境搭建[总结].pdf

    在 Struts2+Spring+Hibernate 框架中,对 Action 的单元测试环境的搭建是一个非常重要的步骤。本文将详细介绍如何在 Struts2+Spring+Hibernate 框架中搭建 Action 的单元测试环境。 首先,需要了解为什么需要对 ...

Global site tag (gtag.js) - Google Analytics