<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
	<channel>
		<title><![CDATA["Open Session In View探讨(转)" 主题的最后发表文章]]></title>
		<link>http://www.mydwbi.com/posts/list/18.page</link>
		<description><![CDATA[最后发表在 "Open Session In View探讨(转)" 主题的信息]]></description>
		<generator>JForum - http://www.jforum.net</generator>
			<item>
				<title>Open Session In View探讨(转)</title>
				<description><![CDATA[ 在没有使用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问题.<br /> <br />     它有两种配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具体参看SpringSide)，功能相同，只是一个在web.xml配置，另一个在application.xml配置而已。<br /> <br />     Open Session In View在request把session绑定到当前thread期间一直保持hibernate session在open状态，使session在request的整个期间都可以使用，如在View层里PO也可以lazy loading数据，如 ${ company.employees }。当View 层逻辑完成后，才会通过Filter的doFilter方法或Interceptor的postHandle方法自动关闭session。<br /> [code]&lt;beans&gt; <br />   &lt;bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor"&gt; <br /> <br />        &lt;property name="sessionFactory"&gt; <br />                 &lt;ref bean="sessionFactory"/&gt;<br />            &lt;/property&gt; <br /> &lt;/bean&gt;  <br />  &lt;bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&gt;     <br />                 &lt;property name="interceptors"&gt; <br />                      &lt;list&gt;<br />                          &lt;ref bean="openSessionInViewInterceptor"/&gt;<br />                    &lt;/list&gt;     <br />                 &lt;/property&gt; <br />               &lt;property name="mappings"&gt; <br />                   ...     <br />               &lt;/property&gt;<br />    &lt;/bean&gt; <br />   ... <br /> &lt;/beans&gt; <br /> [/code]<br /> OpenSessionInViewFilter配置<br /> [code]<br /> &lt;web-app&gt; <br />   ... <br />   &lt;filter&gt;<br />      &lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;<br />      &lt;filter-class&gt;       org.springframework.orm.hibernate3.support.OpenSessionInViewFilter     &lt;/filter-class&gt;<br />      &lt;!-- singleSession默认为true,若设为false则等于没用OpenSessionInView --&gt;<br />     &lt;init-param&gt;<br />        &lt;param-name&gt;singleSession&lt;/param-name&gt;<br />        &lt;param-value&gt;true&lt;/param-value&gt; <br />       &lt;/init-param&gt;<br />   &lt;/filter&gt;<br />  ...<br />    &lt;filter-mapping&gt;<br />      &lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;<br />      &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />    &lt;/filter-mapping&gt; ... &lt;/web-app&gt; <br /> [/code]<br /> <br /> 很多人在使用OpenSessionInView过程中提及一个错误：<br /> <br /> [code]<br /> <br /> org.springframework.dao.InvalidDataAccessApiUsageException: <br /> Write operations are not allowed in read-only mode (FlushMode.NEVER) <br /> - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition <br /> [/code]<br /> 看看OpenSessionInViewFilter里的几个方法<br /> <br /> [code]<br /> protected void doFilterInternal(HttpServletRequest request,<br /> 			 HttpServletResponse response,<br /> 			 FilterChain filterChain) throws ServletException, IOException {<br /> 			 　SessionFactory sessionFactory = lookupSessionFactory();<br /> 			 　logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");<br /> 			 　Session session = getSession(sessionFactory);<br /> 			 　TransactionSynchronizationManager.bindResource(　<br /> 			 　sessionFactory, new SessionHolder(session));<br /> 			 　try {<br /> 			 　　filterChain.doFilter(request, response);　<br /> 			 }　finally {　<br /> 			 TransactionSynchronizationManager.unbindResource(sessionFactory);<br /> 			 　logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");<br /> 			 　closeSession(session, sessionFactory);<br /> 			 　}<br /> 			 }<br /> <br />  protected Session getSession(SessionFactory sessionFactory)throws DataAccessResourceFailureException {<br />  　Session session = SessionFactoryUtils.getSession(sessionFactory, true);<br />  　session.setFlushMode(FlushMode.NEVER);<br />  　return session;<br />  }<br />  <br />  protected void closeSession(Session session, SessionFactory sessionFactory)throws CleanupFailureDataAccessException {<br />  <br />  　SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);<br />  } <br /> [/code]<br /> <br />  可以看到OpenSessionInViewFilter在getSession的时候,会把获取回来的session的flush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到TransactionSynchronizationManager，使request的整个过程都使用同一个session，在请求过后再接除该sessionFactory的绑定，最后closeSessionIfNecessary根据该session是否已和transaction绑定来决定是否关闭session。在这个过程中，若HibernateTemplate 发现自当前session有不是readOnly的transaction，就会获取到FlushMode.AUTO Session，使方法拥有写权限。<br /> [code]<br /> public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory)      throws CleanupFailureDataAccessException {<br /> <br />     if (session == null || TransactionSynchronizationManager.hasResource(sessionFactory)) {<br />           return;    <br />      }<br />      <br />    logger.debug("Closing Hibernate session");<br />    <br />    try {<br />          session.close();    <br />    }    catch (JDBCException ex) {      // SQLException underneath    <br />      throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException());   <br />   }    catch (HibernateException ex) {    <br /> <br />   throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex);   <br />    } <br />    <br />   }<br />  [/code]<br /> <br /> 也即是，如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有insert,update,delete操作权限，如果没有transaction，并且没有另外人为地设flush model的话，则doFilter的整个过程都是Flush.NEVER。所以受transaction保护的方法有写权限，没受保护的则没有。<br /> [code]<br /> &lt;bean id="baseTransaction" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"           abstract="true"&gt;<br />          &lt;property name="transactionManager" ref="transactionManager"/&gt;<br />                   &lt;property name="proxyTargetClass" value="true"/&gt;<br />                            &lt;property name="transactionAttributes"&gt;<br />                                         &lt;props&gt;<br />                                                &lt;prop key="get*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;<br />                                                 &lt;prop key="find*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;<br />                                                 &lt;prop key="load*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;<br />                                                 &lt;prop key="save*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br />                                                 &lt;prop key="add*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br />                                                 &lt;prop key="update*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br />                                                 &lt;prop key="remove*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br />                                          &lt;/props&gt;<br />                             &lt;/property&gt;<br /> &lt;/bean&gt;<br /> &lt;bean id="userService" parent="baseTransaction"&gt;<br />          &lt;property name="target"&gt;<br />          &lt;bean class="com.phopesoft.security.service.impl.UserServiceImpl"/&gt;<br />          &lt;/property&gt;<br /> &lt;/bean&gt;<br /> [/code]<br /> <br /> 对于上例，则以save,add,update,remove开头的方法拥有可写的事务，如果当前有某个方法，如命名为importExcel()，则因没有transaction而没有写权限，这时若方法内有insert,update,delete操作的话，则需要手动设置flush model为Flush.AUTO,如<br /> <br /> ssion.setFlushMode(FlushMode.AUTO);<br />   session.save(user);<br />   session.flush(); <br /> <br />    尽管Open Session In View看起来还不错，其实副作用不少。看回上面OpenSessionInViewFilter的doFilterInternal方法代码，这个方法实际上是被父类的doFilter调用的，因此，我们可以大约了解的OpenSessionInViewFilter调用流程: request(请求)-&gt;open session并开始transaction-&gt;controller-&gt;View(Jsp)-&gt;结束transaction并close session.<br /> <br />      一切看起来很正确，尤其是在本地开发测试的时候没出现问题，但试想下如果流程中的某一步被阻塞的话，那在这期间connection就一直被占用而不释放。最有可能被阻塞的就是在写Jsp这步，一方面可能是页面内容大，response.write的时间长，另一方面可能是网速慢，服务器与用户间传输时间久。当大量这样的情况出现时，就有连接池连接不足，造成页面假死现象。<br /> <br /> Open Session In View是个双刃剑，放在公网上内容多流量大的网站请慎用。<br /> ]]></description>
				<guid isPermaLink="true">http://www.mydwbi.com/posts/preList/9/23.page</guid>
				<link>http://www.mydwbi.com/posts/preList/9/23.page</link>
				<pubDate><![CDATA[Thu, 24 Apr 2008 09:28:48]]> GMT</pubDate>
				<author><![CDATA[ JetGeng]]></author>
			</item>
			<item>
				<title>回复:Open Session In View探讨(转)</title>
				<description><![CDATA[ 类似Open Session In View这种暴露领域模型用法，以前也碰到过，也就是写一个filter来close session,但是事务却一定要放到业务层做，所以一定情况下，还是推荐脱钩的使用pojo。]]></description>
				<guid isPermaLink="true">http://www.mydwbi.com/posts/preList/9/2281.page</guid>
				<link>http://www.mydwbi.com/posts/preList/9/2281.page</link>
				<pubDate><![CDATA[Tue, 14 Oct 2008 03:43:37]]> GMT</pubDate>
				<author><![CDATA[ ibudo]]></author>
			</item>
	</channel>
</rss>
