標籤:
首先我們來看一下什麼是OpenSessionInView?
在hibernate中使用load方法時,並未把資料真正擷取時就關閉了session,當我們真正想擷取資料時會迫使load載入資料,而此時session已關閉,所以就會出現異常。 比較典型的是在MVC模式中,我們在M層調用持久層擷取資料時(持久層用的是load方法載入資料),當這一調用結束時,session隨之關閉,而我們希望在V層使用這些資料,這時才會迫使load載入資料,我們就希望這時的session是open著得,這就是所謂的Open Session In view 。 我們可以用filter來達到此目的。 這段話引至於百度百科,但確實很好的說明了OpenSessionInView這個過濾器的作用。
OpenSessionInViewFilter是Spring提供的一個針對Hibernate的一個支援類,其主要意思是在發起一個頁面請求時開啟Hibernate的Session,一直保持這個Session,直到這個請求結束,具體是通過一個Filter來實現的。 由於Hibernate引入了Lazy Load特性,使得脫離Hibernate的Session周期的對象如果再想通過getter方法取到其關聯對象的值,Hibernate會拋出一個LazyLoad的Exception。所以為瞭解決這個問題,Spring引入了這個Filter,使得Hibernate的Session的生命週期變長。
首先分析一下它的源碼,可以發現,它所實現的功能其實比較簡單:
[java] view plaincopyprint?
- SessionFactory sessionFactory = lookupSessionFactory(request);
- Session session = null;
- boolean participate = false;
-
- if (isSingleSession()) {
- // single session mode
- if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
- // Do not modify the Session: just set the participate flag.
- participate = true;
- } else {
- logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
- session = getSession(sessionFactory);
- TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
- }
- } else {
- // deferred close mode
- if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
- // Do not modify deferred close: just set the participate flag.
- participate = true;
- } else {
- SessionFactoryUtils.initDeferredClose(sessionFactory);
- }
- }
-
- try {
- filterChain.doFilter(request, response);
- } finally {
- if (!participate) {
- if (isSingleSession()) {
- // single session mode
- TransactionSynchronizationManager.unbindResource(sessionFactory);
- logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
- closeSession(session, sessionFactory);
- }else {
- // deferred close mode
- SessionFactoryUtils.processDeferredClose(sessionFactory);
- }
- }
- }
SessionFactory sessionFactory = lookupSessionFactory(request);Session session = null;boolean participate = false;if (isSingleSession()) {// single session modeif (TransactionSynchronizationManager.hasResource(sessionFactory)) {// Do not modify the Session: just set the participate flag.participate = true; }else {logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");session = getSession(sessionFactory);TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));}} else {// deferred close modeif (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {// Do not modify deferred close: just set the participate flag.participate = true; } else {SessionFactoryUtils.initDeferredClose(sessionFactory); }}try {filterChain.doFilter(request, response);} finally {if (!participate) { if (isSingleSession()) { // single session modeTransactionSynchronizationManager.unbindResource(sessionFactory);logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");closeSession(session, sessionFactory);}else {// deferred close modeSessionFactoryUtils.processDeferredClose(sessionFactory);}}}
在上述代碼中,首先獲得SessionFactory,然後通過SessionFactory獲得一個Session。然後執行真正的Action代碼,最後根據情況將Hibernate的Session進行關閉。整個思路比較清晰。
下面我們來看一下他的具體配置,其實很簡單,直接在web.xml中把他這個filter配置上就ok了:
[html] view plaincopyprint?
- <filter>
- <filter-name>openSessionInView</filter-name>
- <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
- <init-param>
- <param-name>sessionFactoryBeanName</param-name>
- <param-value>sf</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>openSessionInView</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
<filter><filter-name>openSessionInView</filter-name><filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class><init-param><param-name>sessionFactoryBeanName</param-name><param-value>sf</param-value></init-param></filter><filter-mapping><filter-name>openSessionInView</filter-name><url-pattern>/*</url-pattern></filter-mapping>
在上面配置中我們要注意以下幾點:
1、這個filter一定要配置在struts的過濾器的前面,因為過濾器是“先進後出”原則,如果你配置在struts的後面的話,你的openSessionInView過濾器都執行完了,怎麼在去在管理action的轉向頁面啊。
2、Opensessioninview也需要sessionfactory的bean的注入,他預設的去找bean的id為sessionfactory的bean,如果sessionfactory的bean的id不是這個名字的話,要記得給這個過濾器配置一個參數,參數名為sessionfactoryBeanName,把他的value設定為你的sessionfactory的bean的id值。
3、在用opensessioninview的時候一定要注意,如果你不配置transaction的話,在用opensessioninview時,他預設的把事務配置為only-read唯讀,這樣的話,如果你進行增刪改的時候,他就會報一個在唯讀事務中不能進行增刪改的操作。如果把opensessioninview去掉,他預設的事務的開始邊界就鎖定在dao層操作上,dao層hibernatetempt提供了事務的開始和提交
OpenSessionInView的副作用
瞭解了上面幾個問題之後,那麼也就可以大概知道OpenSessionInView的副作用 – 資源佔用嚴重,配置不當,影響系統效能。使用OpenSessionInView後,在request發出和response返回的流程中,如果有任何一步被阻塞,那在這期間connection就會被一直佔用而不釋放。比如頁面較大,顯示需要時間 或者 網速太慢,伺服器與使用者間傳輸的時間太長,這都會導致資源佔用,最直接的表現就是串連池串連不夠用,而導致最終伺服器無法提供服務。
[轉]Java程式員從笨鳥到菜鳥之(八十三)細談Spring(十二)OpenSessionInView詳解及用法