no Session問題,即延遲加載
在開發中,相信很多同學都遇到過這個問題,也就是說當我們在前台頁面用表達式語言取值的時候,會發現程序后台報錯了,顯示的錯誤時no session,其實這個就是延遲加載的問題,下面進行簡單的談一談,並提出幾點解決的建議。
延遲加載的問題是指當我們調用完action中的某個方法,在jsp頁面要顯示我們想要的信息的時候,發現在dao中打開的session已經關閉了。
如下圖,第一個箭頭表示的是我們通過前台頁面返回action,action會通過service層調用dao去訪問數據庫,當從數據庫中把值取出來之后返回到action中,再返回到前台頁面中去。我們知道,只有我們在調用某個類getter()方法的時候才會打開session,可惜的是,在hibernateTemplate中注入的sessionFactory在action返回到前台頁面前已經關閉了,也就是這個時候再前台頁面調用getter()方法,想要打開session,但是sessionFactory已經關閉了,所以后台就會報錯,顯示no session。意思就是找不到session。
這個時候可以采用以下幾種方式解決:
1、 將pojo類中的fetch的lazy改成eager,這個時候如果你的操作頁面中牽扯了多個外鍵關系的話,就要把這幾個外鍵關系的fetch的lazy都改成eager。這里的lazy是代表懶加載的意思,也就是說在查詢一個表的信息的時候,不會把關聯的表的信息查找出來。而如果改成eager的話,就代表說把關聯的表的信息也全部加載出來。很明顯,這樣的操作會加載一些我們壓根就不需要的操作,也可能我們只需要加載某張表的信息的時候,卻自動的把關聯的表加載出來。所以這種操作性能是很差的,不建議使用。
2、 配置過濾器openSessionInViewFilter,顧名思義,這個過濾器是指把在頁面中打開session的服務,也就是說延遲session打開的狀態,在前台頁面中調用完方法之后再關閉session,配置如下:
<filter> <filter-name>OpenSessionInView</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
要注意的是這個過濾器要配置到struts的過濾器的前面,不然就不起作用了,因為配置在struts過濾器的后面的話,數據已經接收完畢了,也就是說sessionFactory已經關閉了,這個時候你再來配置說把session延長到view中,已經沒作用了,所以要把openSessionInViewFilter配置在struts前面。但是要注意的是,上面的配置並不是完整的配置,完整的配置應該是在filter里面配置如下信息:
<filter> <filter-name>OpenSessionInView</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> <init-param> <param-name>singleSession</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>OpenSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
上面配置的意思是表示配置成單例的session。但是如果按以上配置的話,在后續session的操作中是會出現問題的,而如果不配置成單例的話,性能是很低的。所以這個方法也不建議。
3、 手工操作(建議)
a) 在action中操作
在action中操作指的是我們在傳遞對象到前台頁面(這里主要是jsp)的時候,多傳遞一些對象到前台去。比如在action中我們需要傳遞question對象到前台中進行展示,但是同時也需要查找出這個問題所屬的類型type,這個時候就可以在頁action中除了傳遞question對象之外,再根據這個question的其他屬性(如tid)來查找到對應的type,再把type對象傳遞到頁面中。不過這樣做比較麻煩的是除了自己要在dao中加方法外,如果一個頁面管理着好幾個數據表的話,傳遞的對象屬性相對就比較多了。
b) 在daoimpl中操作(強烈建議)
在daoimpl中進行手工設置是本人覺得最合適的方式,也是比較推崇的一種。用這種方法的話,在daoimpl的操作中,就要用到hibernateCallback的內部類方法了。也就是說調用super.getHibernateTemplate().execute()方法,編寫內部類,在得到一個list集合之后,取出集合的對象,調用要用到的關聯類的getter()方法,就可以打開session了,一般不用調用主鍵的getter()方法。
范例(只是部分代碼,無法運行,僅供參考):
1 public Question findById(final Integer id) throws Exception { 2 return super.getHibernateTemplate().execute( 3 new HibernateCallback<Question>() { 4 @SuppressWarnings("unchecked") 5 public Question doInHibernate(Session session) 6 throws HibernateException, SQLException { // 用hibernateCallBack 7 String hql = "From Question AS q WHERE q.qid=?"; // 構建hql語句 8 Query query = session.createQuery(hql); // 創建query 9 query.setInteger(0, id); // 設置占位符 10 List<Question> all = query.list(); // 得到具體的list集合,里面只存放一個question對象 11 if (all.size() > 0) { // 集合不為空 12 Question vo = all.get(0); // 取出question對象 13 vo.getType().getTitle(); // 調用type的getTitle方法,這樣會把type相關的session打開,在jsp調用時數據還可以調用 14 vo.getAnswers().size(); // 調用getAnswers()方法,把session打開,消除延遲加載 15 if (vo.getAnswer() != null) { // 如果存在最佳答案 16 vo.getAnswer().getContent(); // 消除延遲加載 17 } 18 return vo; // 返回vo 19 } 20 return null; 21 } 22 }); 23 }