本文共 6776 字,大约阅读时间需要 22 分钟。
<p>Spring+Hibernate中的(FlushMode.NEVER)错误产生及解决<br><a href="http://www.firnow.com">www.firnow.com</a> 时间 : 2007-10-18 作者:佚名 编辑:本站 点击: 1597 [ 评论 ]<br>-<br>-</p> <p>在没有使用Spring提供的Open Session In View情况下,因需要在service(or Dao)层里把session关闭,所以lazy loading 为true的话,要</p> <p>在应用层内把关系集合都初始化,如 company.getEmployees(),否则Hibernate抛session already closed Exception; Open Session In </p> <p>View提供了一种简便的方法,较好地解决了lazy loading问题.</p> <p> 它有两种配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具体参看SpringSide),功能相同,只是一个在web.xml配</p> <p>置,另一个在application.xml配置而已。</p> <p> Open Session In View在request把session绑定到当前thread期间一直保持hibernate session在open状态,使session在request的整个期</p> <p>间都可以使用,如在View层里PO也可以lazy loading数据,如 ${ company.employees }。当View 层逻辑完成后,才会通过Filter的doFilter</p> <p>方法或Interceptor的postHandle方法自动关闭session。</p> <p></p> <p>OpenSessionInViewInterceptor配置<beans> <br><bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor"> </p> <p><br><property name="sessionFactory"> <br><ref bean="sessionFactory"/> <br></property> <br></bean> <br><bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <br><property name="interceptors"> <br><list> <br><ref bean="openSessionInViewInterceptor"/> <br></list> <br></property> <br><property name="mappings"> <br>... <br></property> <br></bean> <br>... <br></beans> <br>OpenSessionInViewFilter配置<br><web-app><br>... <br><filter> <br><filter-name>hibernateFilter</filter-name> <br><filter-class> <br>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter <br></filter-class> <br><!-- singleSession默认为true,若设为false则等于没用OpenSessionInView --> <br><init-param> <br><param-name>singleSession</param-name> <br><param-value>true</param-value> <br></init-param> <br></filter> <br>... <br><filter-mapping> <br><filter-name>hibernateFilter</filter-name> <br><url-pattern>*.do</url-pattern> <br></filter-mapping><br>... <br></web-app> <br>很多人在使用OpenSessionInView过程中提及一个错误:</p> <p>org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode </p> <p>(FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition 看看</p> <p>OpenSessionInViewFilter里的几个方法</p> <p>protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain) throws </p> <p>ServletException, IOException <br>{ <br>SessionFactory sessionFactory = lookupSessionFactory(); <br>logger.debug("Opening Hibernate Session in OpenSessionInViewFilter"); <br>Session session = getSession(sessionFactory); <br>TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); <br>try<br>{ filterChain.doFilter(request, response); <br>} <br>finally <br>{ <br>TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing Hibernate Session in </p> <p>OpenSessionInViewFilter"); <br>closeSession(session, sessionFactory); <br>}<br>} <br>protected Session getSession(SessionFactory sessionFactory)throws DataAccessResourceFailureException <br>{ <br>Session session = SessionFactoryUtils.getSession(sessionFactory, true); <br>session.setFlushMode(FlushMode.NEVER); <br>return session;<br>}<br>protected void closeSession(Session session, SessionFactory sessionFactory)throws CleanupFailureDataAccessException<br>{ <br>SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);<br>} <br>可以看到OpenSessionInViewFilter在getSession的时候,会把获取回来的session的flush mode 设为FlushMode.NEVER。<br>然后把该sessionFactory绑定到TransactionSynchronizationManager,使request的整个过程都使用同一个session,<br>在请求过后再接除该sessionFactory的绑定,最后closeSessionIfNecessary根据该session是否已和transaction绑定来决定是否关闭session</p> <p>。在这个过程中,若HibernateTemplate 发现自当前session有不是readOnly的transaction,就会获取到FlushMode.AUTO Session,使方法拥</p> <p>有写权限。</p> <p>public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory) throws </p> <p>CleanupFailureDataAccessException <br>{ <br>if (session == null || TransactionSynchronizationManager.hasResource(sessionFactory)) { <br>return; <br>} <br>logger.debug("Closing Hibernate session"); <br>try { <br>session.close(); <br>} <br>catch (<br>JDBCException ex) <br>{ <br>// SQLException underneath <br>throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException()); <br>} <br>catch (HibernateException ex) <br>{ <br>throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex); <br>} <br>} <br>也即是,如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有insert,update,delete操作权限,如果没有</p> <p>transaction,并且没有另外人为地设flush model的话,则doFilter的整个过程都是Flush.NEVER。所以受transaction保护的方法有写权限,</p> <p>没受保护的则没有。</p> <p>采用spring的事务声明,使方法受transaction控制 <br><bean id="baseTransaction" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" </p> <p>abstract="true"> <br><property name="transactionManager" ref="transactionManager"/> <br><property name="proxyTargetClass" value="true"/> <br><property name="transactionAttributes"> <br><props> <br><prop key="get*"><br>PROPAGATION_REQUIRED,readOnly<br></prop> <br><prop key="find*"><br>PROPAGATION_REQUIRED,readOnly<br></prop> </p> <p><prop key="load*">PROPAGATION_REQUIRED,readOnly<br></prop> <br><prop key="save*">PROPAGATION_REQUIRED</prop> <br><prop key="add*">PROPAGATION_REQUIRED</prop> <br><prop key="update*">PROPAGATION_REQUIRED</prop> <br><prop key="remove*">PROPAGATIO</p> <p><br>Spring+Hibernate中的(FlushMode.NEVER)错误产生及解决<br><a href="http://www.firnow.com">www.firnow.com</a> 时间 : 2007-10-18 作者:佚名 编辑:本站 点击: 1598 [ 评论 ]<br>-<br>-<br>N_REQUIRED</prop> <br></props> <br></property> <br></bean> <br><bean id="userService" parent="baseTransaction"> <br><property name="target"> <br><bean class="com.phopesoft.security.service.impl.UserServiceImpl"/> <br></property> <br></bean> <br>对于上例,则以save,add,update,remove开头的方法拥有可写的事务,如果当前有某个方法,如命名为importExcel(),则因没有transaction</p> <p>而没有写权限,这时若方法内有insert,update,delete操作的话,则需要手动设置flush model为Flush.AUTO,如<br>session.setFlushMode(FlushMode.AUTO); <br>session.save(user); <br>session.flush(); <br>尽管Open Session In View看起来还不错,其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternal方法代码,这个方法实际</p> <p>上是被父类的doFilter调用的,因此,我们可以大约了解的OpenSessionInViewFilter调用流程: request(请求)->open session并开始</p> <p>transaction->controller->View(Jsp)->结束transaction并close session.<br>一切看起来很正确,尤其是在本地开发测试的时候没出现问题,但试想下如果流程中的某一步被阻塞的话,那在这期间connection就一直被占</p> <p>用而不释放。最有可能被阻塞的就是在写Jsp这步,一方面可能是页面内容大,response.write的时间长,另一方面可能是网速慢,服务器与用</p> <p>户间传输时间久。当大量这样的情况出现时,就有连接池连接不足,造成页面假死现象。</p> <p>Open Session In View是个双刃剑,放在公网上内容多流量大的网站请慎用 </p> <p><br><a href="http://www.firnow.com):http://dev.firnow.com/course/3_program/java/javajs/20071018/77920.html"></a></p>转载地址:http://mcaji.baihongyu.com/