duansky'weblog

          統(tǒng)計

          留言簿(3)

          友情鏈接

          閱讀排行榜

          評論排行榜

          Spring與Hibernate集成中的session問題

          1.通過getSession()方法獲得session進(jìn)行操作
          Java代碼 復(fù)制代碼
          1. public ? class ?Test?? extends ?HibernateDaoSupport{ ??
          2. ????? public ? void ?save(User?user){ ??
          3. ???????? this .getSession().save(user); ??
          4. ?????} ??
          5. }????

          利用這種方式獲得的session在方法執(zhí)行結(jié)束之后不會自動關(guān)閉連接,也就是說我們必須通過session.close()或者releaseSession(session)來手動進(jìn)行關(guān)閉,否則會造成內(nèi)存泄露或者連接耗盡等問題。手動關(guān)閉:
          Java代碼 復(fù)制代碼
          1. public ? class ?Test?? extends ?HibernateDaoSupport{ ??
          2. ????? public ? void ?save(User?user){ ??
          3. ????????Session?session?=? this .getSession(); ??
          4. ????????session.save(user); ??
          5. ????????session.close(); ??
          6. ???????? //?releaseSession(session);? ??
          7. ?????} ??
          8. }???

          如果對上述方法進(jìn)行事務(wù)控制,那么spring框架會自動為我們關(guān)閉session,此種情況下再執(zhí)行上述代碼,會拋出如下異常:
          Java代碼 復(fù)制代碼
          1. ?org.springframework.orm.hibernate3.HibernateSystemException:?Session?is?closed;?nested?exception?is?org.hibernate.SessionException:?Session?is?closed ??
          2. … ??
          3. org.hibernate.SessionException:?Session?is?closed??

          提示session已經(jīng)關(guān)閉。但是如果在代碼中通過releaseSession(session)的方法來關(guān)閉session,則不會拋出異常。releaseSession(session)方法的代碼如下:
          Java代碼 復(fù)制代碼
          1. protected ? final ? void ?releaseSession(Session?session)?{ ??
          2. ????SessionFactoryUtils.releaseSession(session,?getSessionFactory()); ??
          3. }??

          也就是說它是通過SessionFactoryUtils的releaseSession方法來實現(xiàn)的:
          Java代碼 復(fù)制代碼
          1. public ? static ? void ?releaseSession(? ??
          2. ?????Session?session,SessionFactory?sessionFactory)?{ ??
          3. ?????????? if ?(session?==? null )?{ ??
          4. ?????????????? return ; ??
          5. ??????????} ??
          6. ?????????? //?Only?close?non-transactional?Sessions. ??
          7. ?????????? if ?(!isSessionTransactional(session,sessionFactory))???{ ??
          8. ?????????????closeSessionOrRegisterDeferredClose??(session,?sessionFactory); ??
          9. ??????????} ??
          10. ????}??

          可見它內(nèi)部會先進(jìn)行判斷。

          查看getSession()方法的源碼:
          Java代碼 復(fù)制代碼
          1. protected ? final ?Session?getSession() ??
          2. ???????? throws ?DataAccessResourceFailureException,?IllegalStateException?{ ??
          3. ??
          4. ???????? return ?getSession( this .hibernateTemplate.isAllowCreate()); ??
          5. }??

          getSession()方法內(nèi)部通過它的一個重載方法getSession(boolean allowCreate )來實現(xiàn),變量allowCreate是HibernateTemplate中的變量,默認(rèn)值為true,也就是創(chuàng)建一個新的session。如果我們調(diào)用getSession(false)來獲得session,那么必須對其進(jìn)行事務(wù)控制,原因是:(spring文檔)
          Java代碼 復(fù)制代碼
          1. protected ?? final ??org.hibernate.Session??getSession()? ??
          2. throws ?DataAccessResourceFailureException,???IllegalStateException?? ??
          3. ??
          4. Get?a?Hibernate?Session,?either?from?the?current?transaction?or?a? new ?one.?The?latter?is?only?allowed? if ?the? "allowCreate" ?setting?of? this ?bean's?HibernateTemplate?is? true .???

          也就是說,getSession()方法從當(dāng)前事務(wù)或者一個新的事務(wù)中獲得session,如果想從一個新的事務(wù)中獲得session(也就意味著當(dāng)其不存在事務(wù)控制),則必須使HibernateTemplate中的allowCreate變量的值為”true”,而現(xiàn)在設(shè)置allowCreate變量的值為”false”就意味著無法從新的事務(wù)中獲得session,也就是只能從當(dāng)前事務(wù)中獲取,所以必須對當(dāng)前方法進(jìn)行事務(wù)控制,否則會拋出如下異常:
          Java代碼 復(fù)制代碼
          1. java.lang.IllegalStateException:?No?Hibernate?Session?bound?to?thread,?and?configuration?does?not?allow?creation?of?non-transactional?one?here?...??

          同時,如果對getSession()所在的方法進(jìn)行事務(wù)控制,那么類似如下的代碼:
          Java代碼 復(fù)制代碼
          1. Session?session?=? null ; ??
          2. for ( int ?m?= 0 ;m< 5 ;m++){ ??
          3. ????Admin?admin?=? new ?Admin(); ??
          4. ????admin.setName( "test" ); ??
          5. ????admin.setPassword( "098" );??? ??
          6. ????session?=? this .getSession(); ??
          7. ????session.save(admin); ??
          8. }??

          只會打開一個session,因為事務(wù)控制必須確保是同一個連接,spring會確保在整個相關(guān)方法中只存在一個session。Spring在方法開始時會打開一個session(即使進(jìn)行事務(wù)控制的方法內(nèi)部不執(zhí)行數(shù)據(jù)庫操作),之后在請求session時,如果在事務(wù)中存在一個未commit的session就返回,以此確保同一個session。

          2.getCurrentSession()與openSession()
          getCurrentSession()與openSession()方法通過Hibernate的SessionFactory獲得,兩者的區(qū)別網(wǎng)上有很多文章已經(jīng)介紹過,即:
          Java代碼 復(fù)制代碼
          1. ①getCurrentSession創(chuàng)建的session會和綁定到當(dāng)前線程,而openSession不會。? ??
          2. ②getCurrentSession創(chuàng)建的線程會在事務(wù)回滾或事物提交后自動關(guān)閉,而openSession必須手動關(guān)閉??


          對于getCurrentSession()方法:
          ??????? (1)其所在方法必須進(jìn)行事務(wù)控制
          ??????? (2)Session在第一次被使用的時候,或者第一次調(diào)用getCurrentSession()的時候,其生命周期就開始。然后它被Hibernate綁定到當(dāng)前線程。當(dāng)事務(wù)結(jié)束的時候,不管是提交還是回滾,Hibernate也會把Session從當(dāng)前線程剝離,并且關(guān)閉它。假若你再次調(diào)用getCurrentSession(),你會得到一個新的Session,并且開始一個新的工作單元。????
          ??
          對于openSession()方法:
          ???????? 這個方法一般在spring與Hibernate的集成中不直接使用,它就是打開一個session,并且這個session與上下文無關(guān),如果對其所在方法進(jìn)行事務(wù)控制,會發(fā)現(xiàn)不起作用,原因就是前面提到的,事務(wù)控制必須確保是同一個連接,而openSession()打開的session與上下文無關(guān)。這個方法與getSession(),getCurrentSession()以及getHibernateTemplate()等方法的區(qū)別在于:后面的幾個方法spring可以對其進(jìn)行控制,如果對它們所在的方法進(jìn)行事務(wù)控制,spring可以確保是同一個連接,而openSession()方法,spring無法對其進(jìn)行控制,所以事務(wù)也不會起作用。


          3.OpenSessionInView
          OpenSessionInView的主要功能是用來把一個Hibernate Session和一次完整的請求過程對應(yīng)的線程相綁定。Open Session In View在request把session綁定到當(dāng)前thread期間一直保持hibernate session在open狀態(tài),使session在request的整個期間都可以使用,如在View層里PO也可以lazy loading數(shù)據(jù),如 ${ company.employees }。當(dāng)View 層邏輯完成后,才會通過Filter的doFilter方法或Interceptor的postHandle方法自動關(guān)閉session。
          Java代碼 復(fù)制代碼
          1. public ? class ?Group? implements ?Serializable{? ??
          2. ???? private ? int ?id;? ??
          3. ???? private ?String?name;? ??
          4. ???? private ?Set?users; ??
          5. ?????????... ??
          6. }??

          在業(yè)務(wù)方法中加載Group對象并將其保存到HttpSession對象中
          Java代碼 復(fù)制代碼
          1. List?groups?=?ht.find( "from?Group" ); ??
          2. Group?group?=?(Group)groups.get( 0 ); ??
          3. HttpSession?session?=?ServletActionContext.getRequest().getSession(); ??
          4. session.setAttribute( "group" ,?group);??

          注意Group采用默認(rèn)的延遲加載機制,即此時返回的只是一個Group代理對象,
          在jsp頁面中顯示group對象的users屬性,如下:

          Java代碼 復(fù)制代碼
          1. <%?? ??
          2. ?????Group?group?=?(Group)session.getAttribute( "group" ); ??
          3. ?????out.println(group.getUsers()); ??
          4. %>???

          此時會拋出如下異常:
          Java代碼 復(fù)制代碼
          1. org.hibernate.LazyInitializationException:?failed?to?lazily?initialize?a?collection?of?role:?entity.Group.users,?no?session?or?session?was?closed??

          延遲加載機制使得在業(yè)務(wù)方法執(zhí)行結(jié)束之后僅僅返回Group的一個代理對象,在jsp頁面中使用到group對象的值時,才發(fā)出sql語句加載,但此時session已經(jīng)關(guān)閉。解決方法是采用OpenSessionInView機制,在web.xml頁面中配置如下過濾器:
          Java代碼 復(fù)制代碼
          1. <filter>?? ??
          2. ???<filter-name>hibernateFilter</filter-name>? ??
          3. ???<filter- class >? ??
          4. org.springframework.orm.hibernate3.support.OpenSessionInViewFilter ??
          5. ???</filter- class >?? ??
          6. </filter>??


          總結(jié):
          (1) 對于getSession(),getSession(false),getCurrentSession()以及getHibernateTemplate()方法而言,如果對其所在方法進(jìn)行事務(wù)控制,那么可以確保在整個方法中只存在一個session,無論你執(zhí)行了幾次CRUD操作,并且所打開的session會在事務(wù)結(jié)束時自動關(guān)閉。
          (2) 必須對getSession(false)以及getCurrentSession()所在的方法進(jìn)行事務(wù)控制(原因見上述分析)
          (3) 如果沒有對getSession()以及getHibernateTemplate()所在方法進(jìn)行事務(wù)控制,那么如果在方法中進(jìn)行N次CRUD操作,就會打開N個session,即每次調(diào)用getSession()和getHibernateTemplate()方法都會打開新的session。這兩個方法的區(qū)別在于:getHibernateTemplate()方法結(jié)束時會自動關(guān)閉連接,而getSession()方法必須手動關(guān)閉。
          (4) 如果在方法中采用SessionFactory的openSession()方法獲得連接進(jìn)行操作,那么無法對其進(jìn)行事務(wù)控制。
          (5) 一般的開發(fā)中,通常采用getHibernateTemplate()方法進(jìn)行數(shù)據(jù)庫操作, getHibernateTemplate()方法采用模板+回調(diào)的機制,進(jìn)行數(shù)據(jù)庫操作很方便,可以查看(其中session的打開與關(guān)閉都是在doExecute方法中進(jìn)行的):

          http://lijiejava.javaeye.com/blog/667644
          http://lijiejava.javaeye.com/blog/727249

          轉(zhuǎn)自: http://www.javaeye.com/topic/733971

          posted on 2010-08-13 17:33 duansky 閱讀(433) 評論(0)  編輯  收藏 所屬分類: Java

          主站蜘蛛池模板: 红安县| 福清市| 神农架林区| 庄河市| 汉中市| 长岛县| 余干县| 改则县| 成安县| 民权县| 阆中市| 陈巴尔虎旗| 四川省| 大邑县| 怀安县| 朔州市| 梅河口市| 彭州市| 商城县| 柳江县| 英山县| 武宣县| 武陟县| 霍邱县| 宜黄县| 阜城县| 唐海县| 台东市| 平乐县| 新乡市| 永嘉县| 勃利县| 武定县| 太湖县| 高阳县| 来宾市| 昌吉市| 南京市| 秦安县| 左云县| 锦州市|