OpenSessionInViewFilter作用及配置
生活随笔
收集整理的這篇文章主要介紹了
OpenSessionInViewFilter作用及配置
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
http://smesoft.blog.51cto.com/2740835/878389
一、作用
Spring為我們解決Hibernate的Session的關(guān)閉與開啟問題。? Hibernate 允許對(duì)關(guān)聯(lián)對(duì)象、屬性進(jìn)行延遲加載,但是必須保證延遲加載的操作限于同一個(gè) Hibernate Session 范圍之內(nèi)進(jìn)行。如果 Service 層返回一個(gè)啟用了延遲加載功能的領(lǐng)域?qū)ο蠼o Web 層,當(dāng) Web 層訪問到那些需要延遲加載的數(shù)據(jù)時(shí),由于加載領(lǐng)域?qū)ο蟮?Hibernate Session 已經(jīng)關(guān)閉,這些導(dǎo)致延遲加載數(shù)據(jù)的訪問異常 (eg: org.hibernate.LazyInitializationException:(LazyInitializationException.java:42)? - failed to lazily initialize a collection of role: cn.easyjava.bean.product.ProductType.childtypes, no session or session was closed? org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.easyjava.bean.product.ProductType.childtypes, no session or session was closed) 用來把一個(gè)Hibernate Session和一次完整的請(qǐng)求過程對(duì)應(yīng)的線程相綁定。目的是為了實(shí)現(xiàn)"Open Session in View"的模式。例如: 它允許在事務(wù)提交之后延遲加載顯示所需要的對(duì)象。 而Spring為我們提供的OpenSessionInViewFilter過濾器為我們很好的解決了這個(gè)問題。OpenSessionInViewFilter的主要功能是用來把一個(gè)Hibernate Session和一次完整的請(qǐng)求過程對(duì)應(yīng)的線程相綁定。目的是為了實(shí)現(xiàn)"Open Session in View"的模式。例如: 它允許在事務(wù)提交之后延遲加載顯示所需要的對(duì)象。? OpenSessionInViewFilter 過濾器將 Hibernate Session 綁定到請(qǐng)求線程中,它將自動(dòng)被 Spring 的事務(wù)管理器探測到。所以 OpenSessionInViewFilter 適用于 Service 層使用HibernateTransactionManager 或 JtaTransactionManager 進(jìn)行事務(wù)管理的環(huán)境,也可以用于非事務(wù)只讀的數(shù)據(jù)操作中。 二、配置 它有兩種配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具體參看SpringSide),功能相同,只是一個(gè)在web.xml配置,另一個(gè)在application.xml配置而已。 Open Session In View在request把session綁定到當(dāng)前thread期間一直保持hibernate session在open狀態(tài),使session在request的整個(gè)期間都可以使用,如在View層里PO也可以lazy loading數(shù)據(jù),如 ${ company.employees }。當(dāng)View 層邏輯完成后,才會(huì)通過Filter的doFilter方法或Interceptor的postHandle方法自動(dòng)關(guān)閉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默認(rèn)為true,若設(shè)為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> 三、注意事項(xiàng) 很多人在使用OpenSessionInView過程中提及一個(gè)錯(cuò)誤: 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里的幾個(gè)方法 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); } } protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); session.setFlushMode(FlushMode.NEVER); return session; }? protected void closeSession(Session session, SessionFactory sessionFactory) throws CleanupFailureDataAccessException { SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory); } 可以看到OpenSessionInViewFilter在getSession的時(shí)候,會(huì)把獲取回來的session的flush mode 設(shè)為FlushMode.NEVER。然后把該sessionFactory綁定到 TransactionSynchronizationManager,使request的整個(gè)過程都使用同一個(gè)session,在請(qǐng)求過后再接除該 sessionFactory的綁定,最后closeSessionIfNecessary根據(jù)該 session是否已和transaction綁定來決定是否關(guān)閉session。在這個(gè)過程中,若HibernateTemplate 發(fā)現(xiàn)自當(dāng)前session有不是readOnly的transaction,就會(huì)獲取到FlushMode.AUTO Session,使方法擁有寫權(quán)限。 public static void closeSessionIfNecessary(Session session, SessionFactory ?sessionFactory)? throws CleanupFailureDataAccessException {? if (session == null || TransactionSynchronizationManager.hasResource(sessionFactory)) { return;? }? logger.debug("Closing Hibernate session"); try {? session.close();? }? catch (JDBCException ex) { // SQLException underneath? throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException());? }? catch (HibernateException ex) { throw new CleanupFailureDataAccessException("Could not close Hibernate session", ?ex);? } }? 也即是,如果有不是readOnly的transaction就可以由Flush.NEVER轉(zhuǎn)為Flush.AUTO,擁有 insert,update,delete操作權(quán)限,如果沒有transaction,并且沒有另外人為地設(shè)flush model的話,則doFilter的整個(gè)過程都是Flush.NEVER。所以受transaction保護(hù)的方法有寫權(quán)限,沒受保護(hù)的則沒有。 采用spring的事務(wù)聲明,使方法受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> 對(duì)于上例,則以save,add,update,remove開頭的方法擁有可寫的事務(wù),如果當(dāng)前有某個(gè)方法,如命名為importExcel(),則因沒有transaction而沒有寫權(quán)限,這時(shí)若方法內(nèi)有insert,update,delete操作的話,則需要手動(dòng)設(shè)置flush model為Flush.AUTO,如 session.setFlushMode(FlushMode.AUTO); session.save(user); session.flush(); 盡 管Open Session In View看起來還不錯(cuò),其實(shí)副作用不少。看回上面OpenSessionInViewFilter的doFilterInternal方法代碼,這個(gè)方法實(shí)際上是被父類的doFilter調(diào)用的,因此,我們可以大約了解的OpenSessionInViewFilter調(diào)用流程: request(請(qǐng)求)->open session并開始transaction->controller->View(Jsp)->結(jié)束transaction并 close session. 一切看起來很正確,尤其是在本地開發(fā)測試的時(shí)候沒出現(xiàn)問題,但試想下如果流程中的某一步被阻塞的話,那在這期間connection就一直被占用而不釋放。最有可能被阻塞的就是在寫Jsp這步,一方面可能是頁面內(nèi)容大,response.write的時(shí)間長,另一方面可能是網(wǎng)速慢,服務(wù)器與用戶間傳輸時(shí)間久。當(dāng)大量這樣的情況出現(xiàn)時(shí),就有連接池連接不足,造成頁面假死現(xiàn)象。 Open Session In View是個(gè)雙刃劍,放在公網(wǎng)上內(nèi)容多流量大的網(wǎng)站請(qǐng)慎用 -------------------------------------------------------------------------- 假設(shè)在你的應(yīng)用中Hibernate是通過spring 來管理它的session.如果在你的應(yīng)用中沒有使用OpenSessionInViewFilter或者OpenSessionInViewInterceptor。session會(huì)在transaction結(jié)束后關(guān)閉。 如果你采用了spring的聲明式事務(wù)模式,它會(huì)對(duì)你的被代理對(duì)象的每一個(gè)方法進(jìn)行事務(wù)包裝(AOP的方式)。如下: <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributes"> <props> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="remove*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> <bean id="manager" parent="txProxyTemplate"> <property name="target"> <bean class="org.appfuse.service.impl.BaseManager"> <property name="dao" ref="dao"/> </bean> </property> </bean> 目標(biāo)類org.appfuse.service.impl.BaseManager 的 ?save *方法的事務(wù)類型PROPAGATION_REQUIRED ?,remove* 方法的事務(wù)類型PROPAGATION_REQUIRED 其他的方法的事務(wù)類型是PROPAGATION_REQUIRED,readOnly。 所以給你的感覺是調(diào)用這個(gè)名為“manager”的bean的方法之后session就關(guān)掉了。 如果應(yīng)用中使用了OpenSessionInViewFilter或者OpenSessionInViewInterceptor,所有打開的session會(huì)被保存在一個(gè)線程變量里。在線程退出前通過 OpenSessionInViewFilter或者OpenSessionInViewInterceptor斷開這些session。 為什么這么做?這主要是為了實(shí)現(xiàn)Hibernate的延遲加載功能。基于一個(gè)請(qǐng)求 一個(gè)hibernate session的原則。 spring中對(duì)OpenSessionInViewFilter的描述如下: 它是一個(gè)Servlet2.3過濾器,用來把一個(gè)Hibernate Session和一次完整的請(qǐng)求過程對(duì)應(yīng)的線程相綁定。目的是為了實(shí)現(xiàn)"Open Session in View"的模式。 例如: 它允許在事務(wù)提交之后延遲加載顯示所需要的對(duì)象。 這個(gè)過濾器和 HibernateInterceptor 有點(diǎn)類似:它是通過線程實(shí)現(xiàn)的。無論是沒有事務(wù)的應(yīng)用,還是有業(yè)務(wù)層事務(wù)的應(yīng)用(通過HibernateTransactionManager 或 JtaTransactionManager的方式實(shí)現(xiàn))它都適用。在后一種情況下,事務(wù)會(huì)自動(dòng)采用由這個(gè)filter綁定的Session來進(jìn)行相關(guān)的操作以及根據(jù)實(shí)際情況完成提交操作。 警告: 如果在你的應(yīng)用中,一次請(qǐng)求的過程中使用了單一的一個(gè)HIbernate Session,在這種情況下,采用這個(gè)filter會(huì)產(chǎn)生一些以前沒遇到的問題。特別需要注意的是通過 Hibernate Session重新組織持久化對(duì)象之間關(guān)系的相關(guān)操作需要在請(qǐng)求的最開始進(jìn)行。以免與已經(jīng)加載的相同對(duì)象發(fā)生沖突。 或者,我們可以通過指定"singleSession"="false"的方式把這個(gè)過濾器調(diào)到延期關(guān)閉模式。這樣在一次請(qǐng)求的過程中不會(huì)使用一個(gè)單一的Session.每一次數(shù)據(jù)訪問或事務(wù)相關(guān) 操作都使用屬于它自己的session(有點(diǎn)像不使用Open Session in View).這些session都被注冊(cè)成延遲關(guān)閉模式,即使是在這一次的請(qǐng)求中它相關(guān)操作已經(jīng)完成。 "一次請(qǐng)求一個(gè)session" 對(duì)于一級(jí)緩存而言很有效,但是這樣可以帶來副作用。例如在saveOrUpdate的時(shí)候或事物回滾之后,雖然它和“no Open Session in View”同樣安全。 但是它卻允許延遲加載。 它會(huì)在spring的web應(yīng)用的上下文根中查找Session工廠。它也支持通過在web.xml中定義的“SessionFactoryBeanName”的init-param元素 指定的Session工廠對(duì)應(yīng)的bean的 名字來查找session工廠。默認(rèn)的bean的名字是"sessionFactory".他通過每一次請(qǐng)求查找一次SessionFactory的方式來避免由初始化順序引起的問題(當(dāng)使用ContextLoaderServlet 來集成spring的時(shí)候 ,spring 的應(yīng)用上下文是在這個(gè)filter 之后才被初始化的)。 默認(rèn)的情況下,這個(gè)filter 不會(huì)同步Hibernate Session.這是因?yàn)樗J(rèn)為這項(xiàng)工作是通過業(yè)務(wù)層的事務(wù)來完成的。而且HibernateAccessors 的FlushMode為FLUSH_EAGER.如果你 想讓這個(gè)filter在請(qǐng)求完成以后同步session.你需要覆蓋它的closeSession方法,在這個(gè)方法中在調(diào)用父類的關(guān)閉session操作之前同步session.此外你需要覆蓋它的getSession() 方法。返回一個(gè)session它的FlushMode 不是默認(rèn)的FlushMode.NEVER。需要注意的是getSession()和closeSession()方法只有在single session的模式中才被調(diào)用。 在myfaces的wiki里提供了OpenSessionInViewFilter的一個(gè)子類如下: public class OpenSessionInViewFilter extends org.springframework.orm.hibernate3.support.OpenSessionInViewFilter { /** * we do a different flushmode than in the codebase * here */ protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); session.setFlushMode(FlushMode.COMMIT); return session; } /** * we do an explicit flush here just in case * we do not have an automated flush */ protected void closeSession(Session session, SessionFactory factory) { session.flush(); super.closeSession(session, factory); } }?
總結(jié)
以上是生活随笔為你收集整理的OpenSessionInViewFilter作用及配置的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ConvertUtils.registe
- 下一篇: springSecurity源码分析——