OpenSessionInView

  Spring 爲我們提供了一個叫做 OpenSessionInViewFilter 的過濾器,他是標準的 Servlet Filter 所以我們把它按照規範配置到 web.xml 中方可使用。使用中我們必須配合使用 Spring 的 HibernateDaoSupport 來進行開發,也就是說,我們的dao層的類都要繼承於 HibernateDaoSupport,從中由 Spring 來控制 Hibernate 的 Session 在請求來的時候開啓,走的時候關閉,保證了我們訪問數據對象時的穩定性。

  1. 在 web.xml 中加入對應過濾器配置文件

複製代碼
<!-- Spring的OpenSessionInView實現 -->
<filter>
    
<filter-name>openSessionInViewFilter</filter-name>
    
<filter-class>
        org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    
</filter-class>
</filter>
<filter-mapping>
    
<filter-name>openSessionInViewFilter</filter-name>
    
<url-pattern>/*</url-pattern>
</filter-mapping>
複製代碼

 

  

  2. 在我們訪問持久層數據是使用 Spring 爲我們的 HibernateDaoSupport 的支持,並使用其中的對應方法操作我們的持久層數據

複製代碼
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class XxxDAO extends HibernateDaoSupport {

    
public void save(Xxx transientInstance) {
        
try {
            getHibernateTemplate().save(transientInstance);
        } 
catch (RuntimeException re) {
            
throw re;
        }
    }
}
複製代碼

   OpenSessionInViewFilter的主要功能是用來把一個Hibernate Session和一次完整的請求過程對應的線程相綁定。Open Session In View在request把session綁定到當前thread期間一直保持hibernate session在open狀態,使session在request的整個期間都可以使用,如在View層裏PO也可以lazy loading數據,如 ${ company.employees }。當View 層邏輯完成後,纔會通過Filter的doFilter方法或Interceptor的postHandle方法自動關閉session。

 

  很多人在使用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裏的幾個方法:

 

複製代碼
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的時候,會把獲取回來的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保護的方法有寫權限,沒受保護的則沒有。

 

  從上述代碼其實可以得到一些對我們的開發有幫助的結論: 
  1)如果使用了OpenSessionInView模式,那麼Spring會幫助你管理Session的開和關,從而你在你的DAO中通過HibernateDaoSupport拿到的getSession()方法,都是綁定到當前線程的線程安全的Session,即拿即用,最後會由Filter統一關閉。 
  2)由於拿到的Hibernate的Session被設置了session.setFlushMode(FlushMode.NEVER); 所以,除非你直接調用session.flush(),否則Hibernate session無論何時也不會flush任何的狀態變化到數據庫。因此,數據庫事務的配置非常重要。(我們知道,在調用org.hibernate.Transaction.commit()的時候會觸發session.flush())我曾經見過很多人在使用OpenSessionInView模式時,都因爲沒有正確配置事務,導致了底層會拋出有關FlushMode.NEVER的異常。

 

  總結:

  OpenSessionInView這個模式使用比較簡單,也成爲了大家在Web開發中經常使用的方法,不過它有時候會帶來一些意想不到的問題,這也是在開發中需要注意的。 
  1. 在Struts+Spring+Hibernate環境中,由於配置的問題導致的模式失效這個問題以前論壇曾經討論過,可以參考一下下面這個帖子:http://www.javaeye.com/topic/15057 

  2. OpenSessionInView的效率問題 
  這個問題也有人在論壇提出過,Robbin曾經做過具體的測試,可以具體參考一下下面這個帖子: http://www.javaeye.com/topic/17501 

  3. 由於使用了OpenSessionInView模式後造成了內存和數據庫連接問題 
  這個問題是我在生產環境中碰到的一個問題。由於使用了OpenSessionInView模式,Session的生命週期變得非常長。雖然解決了Lazy Load的問題,但是帶來的問題就是Hibernate的一級緩存,也就是Session級別的緩存的生命週期會變得非常長,那麼如果你在你的Service層做大批量的數據操作時,其實這些數據會在緩存中保留一份,這是非常耗費內存的。還有一個數據庫連接的問題,存在的原因在於由於數據庫的Connection是和Session綁在一起的,所以,Connection也會得不到及時的釋放。因而當系統出現業務非常繁忙,而計算量又非常大的時候,往往數據連接池的連接數會不夠。這個問題我至今非常頭痛,因爲有很多客戶對數據連接池的數量會有限制,不會給你無限制的增加下去。 

  4. 使用了OpenSessionInView模式以後取數據的事務問題 
  在使用了OpenSessionInView以後,其實事務的生命週期比Session的生命週期來得短,就以爲着,其實有相當一部分的查詢是不被納入到事務的範圍內的,此時是否會讀到髒數據?這個問題我至今不敢確認,有經驗的朋友請指教一下。 

  最後提一下OpenSessionInView模式的一些替代方案,可以使用OpenSessionInViewInterceptor來代替這個Filter,此時可以使用Spring的AOP配置,將這個Interceptor配置到你所需要的層次上去。另外就是隻能使用最古老的Hibernate.initialize()方法進行初始化了。

 

轉載自:

 OpenSessionInViewFilter作用及配置:http://www.yybean.com/opensessioninviewfilter-role-and-configuration

OpenSessionInView詳解:http://www.javaeye.com/topic/32001

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章