當(dāng)hibernate+spring配合使用的時(shí)候,如果設(shè)置了lazy=true,那么在讀取數(shù)據(jù)的時(shí)候,,當(dāng)讀取了父數(shù)據(jù)后,,hibernate會(huì)自動(dòng)關(guān)閉session,,這樣,,當(dāng)要使用子數(shù)據(jù)的時(shí)候,系統(tǒng)會(huì)拋出lazyinit的錯(cuò)誤,,這時(shí)就需要使用spring提供的 OpenSessionInViewFilter, OpenSessionInViewFilter主要是保持Session狀態(tài)知道request將全部頁面發(fā)送到客戶端,,這樣就可以解決延遲加載帶來的問題 3、說說Webwork中使用OpenSessionInView的注意事項(xiàng) web.xml中的配置要注意先后順序,,OpenSessionInViewFilter要在Webwork的filter前面,,否則系統(tǒng)會(huì)報(bào)錯(cuò)。 代碼
對(duì)于OpenSessionInView的配置中,,singleSession應(yīng)該設(shè)置為true,,表示一個(gè)request只能打開一個(gè) session,如果設(shè)置為false的話,,session可以被打開多個(gè),,這時(shí)在update、delete的時(shí)候會(huì)出現(xiàn)打開多個(gè)session的異常,。 但是當(dāng)設(shè)置為true的時(shí)候,,系統(tǒng)的性能會(huì)因?yàn)橛脩舻木W(wǎng)絡(luò)狀況受到影響,當(dāng)request在生成頁面完成后,,session才會(huì)被釋放,,所以如果用戶的網(wǎng)絡(luò)狀況比較差,那么連接池中的鏈接會(huì)遲遲不被回收,,造成內(nèi)存增加,,系統(tǒng)性能受損。但是如果不用這種方法的話,,lazy模式有體現(xiàn)不出它的優(yōu)點(diǎn),,用?不用,??jī)呻y啊 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 在沒有使用Spring提供的Open Session In View情況下,,因需要在service(or Dao)層里把session關(guān)閉,所以lazy loading 為true的話,,要在應(yīng)用層內(nèi)把關(guān)系集合都初始化,,如 company.getEmployees(),否則Hibernate拋session already closed Exception; Open Session In View提供了一種簡(jiǎn)便的方法,,較好地解決了lazy loading問題. 它有兩種配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具體參看SpringSide),,功能相同,只是一個(gè)在web.xml配置,另一個(gè)在application.xml配置而已,。 OpenSessionInView在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配置
OpenSessionInViewFilter配置
很多人在使用OpenSessionInView過程中提及一個(gè)錯(cuò)誤:
看看OpenSessionInViewFilter里的幾個(gè)方法
可以看到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)限,。
也即是,,如果有不是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控制
對(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,如
盡管Open Session In View看起來還不錯(cuò),其實(shí)副作用不少,??椿厣厦鍻penSessionInViewFilter的doFilterInternal方法代碼,這個(gè)方法實(shí)際上是被父類的doFilter調(diào)用的,,因此,,我們可以大約了解的OpenSessionInViewFilter調(diào)用流程: request(請(qǐng)求)->open session并開始transaction->controller->View(Jsp)->結(jié)束transaction并close session. 一切看起來很正確,尤其是在本地開發(fā)測(cè)試的時(shí)候沒出現(xiàn)問題,,但試想下如果流程中的某一步被阻塞的話,,那在這期間connection就一直被占用而不釋放,。最有可能被阻塞的就是在寫Jsp這步,一方面可能是頁面內(nèi)容大,,response.write的時(shí)間長(zhǎng),,另一方面可能是網(wǎng)速慢,服務(wù)器與用戶間傳輸時(shí)間久,。當(dāng)大量這樣的情況出現(xiàn)時(shí),,就有連接池連接不足,造成頁面假死現(xiàn)象,。 Open Session In View是個(gè)雙刃劍,,放在公網(wǎng)上內(nèi)容多流量大的網(wǎng)站請(qǐng)慎用。 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 由于OpenSessionInViewFilter把session綁在當(dāng)前線程上,,導(dǎo)致session的生命周期比事務(wù)要長(zhǎng),這期間所有事務(wù)性操作都在復(fù)用這同一個(gè)session,,由此產(chǎn)生了一些“怪問題”: 1.出現(xiàn)如下錯(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 在把session綁在當(dāng)前線程上的時(shí)候,會(huì)把session的flush mode 設(shè)為FlushMode.NEVER,,因此,如果某個(gè)方法沒有事務(wù)或者有只讀事務(wù),,則不能對(duì)session做insert,update,delete操作,,除非事先把session的flush mode手動(dòng)設(shè)為auto 方案: 1、將singleSession設(shè)為false,,這樣只要改 web.xml,,缺點(diǎn)是Hibernate Session的Instance可能會(huì)大增,使用的JDBC Connection量也會(huì)大增,,如果Connection Pool的maxPoolSize設(shè)得太小,,很容易就出問題。<!-- singleSession默認(rèn)為true,若設(shè)為false則等于沒用OpenSessionInView --> 2,、在控制器中自行管理Session的FlushMode,,麻煩的是每個(gè)有Modify的Method都要多幾行程式 session.setFlushMode(FlushMode.AUTO); session.update(user); session.flush(); |
|