隨筆-12  評論-1  文章-0  trackbacks-0

          看這個鏈接作了解:http://zhaohe162.blog.163.com/blog/static/3821679720110251181721/
          一、HQL查詢的from子句

          from是最簡單的語句,也是最基本的HQL語句。from關鍵字后緊跟持久化類的類名。

          例如:

          from Person  表明從Person持久化類中選出全部的實例

          推薦為Person持久化類的每個實例起別名,例如:

          from Person as p

          p作為Person的實例的別名,因此也應該遵守Java的命名規則:第一個單詞的首字母小寫,后面每個單詞的首字母大寫。

          命名別名時,as關鍵字是可選的,但為了增加可讀性,建議保留。

          from 后還可同時出現多個持久化類,此時將產生一個笛卡爾積或跨表的連接,但實際上這種用法很少使用,因為通常我們可能需要使用跨表的連接,此時可以考慮使用隱式連接或顯示連接,而不是直接在from后緊跟多個表名。

          二、關聯和連接

                 Hibernate使用關聯映射來處理底層數據表之間的連接,一旦我們提供了正確的關聯映射后,當程序通過Hibernate進行持久化訪問時,將可利用Hibernate的關聯來進行連接。

                HQL支持兩種關聯連接(join)的形式:隱式(implicit)與顯示(explicit)。

          隱式連接形式不適用join關鍵字,使用英文點號(.)來隱式連接來關聯實體,而Hibernate底層將自動進行關聯查詢。如下HQL語句:

          from Person p where p.myEvent.title > :title

          上面的p.myEvent屬性的實質是一個持久化實體,因此Hibernate底層隱式地自動進行連接查詢。

          顯示連接則需要使用xxx join關鍵字,例如如下語句:

          //使用顯示連接

          from Person p

          inner join p.myEvent event

          where event.happenDate < :endDate

          使用顯示連接時,可以為相關聯的實體,甚至是關聯集合中的全部元素指定一個別名。

          Hibernate支持的HQL連接類型,可使用如下幾種連接方式:

          inner join(內連接),可簡寫成join。

          left outer join(左外連接),可簡寫成left join。

          right outer join(右外連接),可簡寫成right join。

          full join(全連接),并不常用。

          使用顯示連接時,還可通過HQL的with關鍵字來提供額外的連接條件,例如如下HQL語句:

          from Person p

          inner join p.myEvent event

          with p.id > event.id

          where event.happenDate < :endDate

                 HQL語句中的with關鍵字的作用基本等同于SQL99中on關鍵字的作用:都是用于指定連接條件。通過在HQL語句中使用with關鍵字,可以讓HQL語句執行非等值連接查詢。

                由于表連接的方式都是基于底層SQL來實現的,如果底層不支持這些外連接,那么執行對應的HQL時就會相應地引發異常。

           

          三、隱式連接和顯示連接有如下兩點區別:

          (1)隱式連接底層轉換成SQL92的內連接,顯示連接層將轉換成SQL99的多表連接。

          (2)隱式連接和顯示連接查詢后返回的結果不同。

          當HQL語句中省略select關鍵字時,使用隱式連接查詢返回的結果是多個被查詢實體組成的集合。

          當HQL語句中省略select關鍵字時,使用顯示連接查詢返回的結果也是集合,但集合元素是被查詢持久化對象、所有被關聯的持久化對象所組成的數組。

           注意:

                 對于Hibernate3.2.3以后的版本,如果關聯實體是單個實體或單個的組件屬性,HQL依然可以似乎用英文點號(.)來隱式連接關聯實體或組件;但如果關聯實體是集合(包括1-N關聯、N-N關聯和集合元素時組件等),則必須使用xxx join來顯示連接關聯實體或組件。

                 對于集合屬性的,Hibernate默認采用延遲加載策略,解決辦法:

                 (2.1)可以在Hibernate映射文件中指定lazy="false"來關閉延遲加載。

                 (2.2)使用join fetch,通常無須指定別名,因為相關聯的對象不應當在where子句(或其他任何子句)中使用。而且被關聯的對象也不會再被查詢的結果中直接返回,而是應該通過其父對象來訪問。

          使用fetch關鍵字時,有如下幾個注意點:

                 fetch不應該與setMaxResults()或setFirstResult()共用。因為這些操作是基于結果集的,而在預先抓取集合類時可能包含重復的數據,即無法預先知道精確的行數。

                 fetch不能與獨立的with條件一起使用。

                 如果在一次查詢中fetch多個集合,可以查詢返回笛卡爾積,因此請多加注意。

                 對bag映射而言,同時join fetch多個集合時可能出現非預期結果,因此需要謹慎使用。

                full join fetch 與right join fetch是沒有任何意義的。

          程序里希望預加載那些原本應延遲加載的屬性,則可以通過fetch all properties來強制Hibernate立即抓取這些屬性。例如:

          from Document fetch all properties order by name


          四、HQL查詢的select子句

          (1)select子句用于選擇指定的屬性或直接選擇某個實體,當然select選擇的屬性必須是from后持久化類包含的屬性。

          例如:

          select p.name from Person as p

          (2)select可以選擇任意屬性,即不僅可以選擇持久化類的直接屬性,還可以選擇組件屬性包含的屬性。例如:

          select p.name.firstName from Person as p

          (3)在特殊情況下,如果select后只有一項(包括持久化實例或屬性),則查詢得到的集合元素就是該持久化實例或屬性。

          (4)如果select后有多個項,則每個集合就是選擇出的多項組成的數組。

          例如:

          select p.name,p from Person as p

          執行該HQL語句得到的集合元素時類似于[String,Person]結構的數組,其中第一個元素時Person實例的name屬性,第二個元素時Person實例。

          注意:即使select后的列表項選出某個持久化類的全部屬性,這些屬性依然是屬性,Hibernate不會將這些屬性封裝成對象。只有在select后的列表里給出持久化類的別名(其實就是實例名),Hibernate才會將該項封裝成一個持久化實體。

          (5)select支持將選擇出的屬性存入一個List對象中。

          例如:

          select new list(p.name, p.address) from Person as p

           (6)select支持將選擇出的屬性封裝成對象,前提是該對象擁有支持這些屬性的構造器。

          例如:

          select new ClassTest(p,name, p.address) from Person as p;

          而ClassTest必須有如下的構造器:ClassTest(String s1, String s2)

          (7)select還支持給選中的表達式命名別名。例如:

          select p.name as personName from Person as p

          這種用法與new map 結合使用更普遍。例如:

          select new map(p.name as personName) from Person as p

          執行上面的HQL語句返回的結果是集合,其中集合元素時Map對象,以personName作為Map的key,實際選出的值作為Map的value。


           

          五、HQL查詢的聚集函數

          HQL支持的聚集函數與SQL的完全相同:avg,count,max,min,sum。

          例如,如下的HQL語句:

          select count(*) from Person

          select max(p.page) from Person as p

          select子句還支持字符串連接符、算術連接符,以及SQL函數。例如:

          select p.name||""||p.address from Person as p

          select子句也支持使用distinct和all關鍵字,此時的效果與SQL的效果完全相同。


           

          六、多態查詢

                 HQL語句被設計成能夠理解多態查詢,from后跟持久化類名,不僅會查詢出該持久化類的全部實例,還會查詢出該類的子類的全部實例。

          如下面的查詢語句:from Person as p

                 如果Named接口有多個持久化實現類,下面的語句將返回這些持久化類的全部實例。

          如下面的查詢語句:from Named as n

          注意:上面最后一條查詢,需要多個SQL SELECT 語句,因此無法使用order by子句對結果集排序,從而不允許對這些查詢結果使用Query.scroll()方法。


           

          七、HQL查詢的where子句

          (1)where子句用于篩選選中的結果,縮小選擇的范圍。如果沒有為持久化實例命名別名,則可以直接使用屬性名來引用屬性。

          如下面兩條HQL語句:

          from Person where name like "tom%"

          form Person as p where p.name like "tom%"

          (2)復合屬性表達式加強了where子句的功能,例如,如下的HQL語句:

          from Cat cat where cat.mate.name like "kit%"

          上面語句被翻譯成以下含有內連接的SQL查詢:

          select * from cat_table as table1 cat_table as table2

          where table1.mate = table2.mate

          and table1.name like '''kit%'"

          實際上這種用法使用了隱式連接查詢,從Hibernate3.2.3之后,只有當cat,mate屬性引用的是普通組件屬性或者單獨的關聯實體時才可接著在后面使用點好(.)來引用mate屬性,如cat,mate.name;如果cat,mate是集合屬性,Hibernate3.2.3以后的版本不支持這種用法。

          "="號不僅可以被用來比較屬性的值,也可以用來比較實例。

          select cat,mate from Cat cat, Cat mate where cat.mate = mate

          (3)在進行多態持久化的情況下,class關鍵字用來存取一個實例的鑒別值。嵌入where子句的Java類名,將被作為該類的鑒別值。

          //執行多態查詢時,默認會選出Cat及其所有子類的實例

          //在如下HQL語句中,將只選出DomesticCat類的實例

          from Cat cat where cat.class = DomesticCat

          (4)當where子句中的運算符只支持基本類型或者字符串時,where子句中的屬性表達式必須以基本類型或者字符串結尾,不要使用組件類型屬性結尾,例如Account和Person屬性,而Person有Name屬性,Name屬性有firstName屬性。

          如下所示:

          //firstName是字符串

          from Account as a where a.person.name.firstName like "dd%"

          //下面是錯誤實例

          from Account as a where a.person.name like "dd%"


           

          八、表達式

          1、HQL的功能非常豐富,where子句后支持的運算符,不僅包括SQL的運算符,也包括EJB-QL的運算符等。

          where子句中允許使用大部分SQL支持的表達式,包括如下種類:

          (1)字符串連接:如value1||value2,或使用字符串連接函數concat(value1, value2)

          (2)簡單的case,case...when...then...else...end和case,case when...then...else...end等。

          (3)時間操作函數:current_date()、current_time、year()等。

          (4)EJB-QL3.0的函數:substring()、trim()、abs()、sqrt()等。

          (5)可在where子句中使用SQL常量。

          (6)在HQL語句中使用Java中的public static final類型的常量,例如Color.RED

          (7)HQL語句支持使用英文問號(?)作為參數占位符,這與JDBC的參數占位符一致;也使用命名參數占位符號,方法在參數名前加英文冒號(:),例如:start_date等。

          (8)如果底層數據庫支持單行函數,則HQL語句也完全可以支持。

          (9)支持數據庫的類型轉換函數,如cast(... as ...),第二個參數是Hibernate的類型名,或者extract(... from ,,,),前提是底層數據庫支持ANSI cast()和extract()。

          2、如果在Hibernate配置文件中進行如下聲明:

          <property name="hibernate.query.substitutions">true 1,false 0</property>

          上面的聲明表明:HQL轉換SQL語句時,將使用字符1和0來取代關鍵字true和false,然后將可以在表達式中使用布爾表達式。

          3、有用的elements()和indices函數,用于返回指定集合的所有元素和所有索引。

          4、在where子句中,有序集合(數組、List集合、Map對象)的元素可以通過【】運算符來訪問。如下:

          from Order order where order.items[0].id=1234

          在【】中的表達式甚至可以是一個算式表達式。

          5、結構變量:size、elements、indices等,只能在where子句中使用。


           

          九、order by子句

          查詢返回的集合根據類或組件屬性的任何屬性進行排序。例如:

          from Person as p order by p.name,p.age

          還可以使用as或desc關鍵字指定升序或降序的排序規則。例如:

          from Person as p order by p.name asc,p.age desc

          如果沒有指定排序規則,默認采用升序規則。

          十、group by子句

          返回聚集值的查詢可以對持久化類或組件屬性的進行分組,分組使用group by子句。看下面的HQL查詢語句:

          select cat,color,sum(cat.weight),count(cat) from Cat cat group by cat.color

          其規則類似于SQL規則。

          having子句用于對分組進行過濾,如下所示:

          select cat,color,sum(cat.weight),count(cat) from Cat cat group by cat.color having cat.color in (eg.Color.TABBY,eg.Color.BLACK)

          注意:group by子句與order by 子句中都不能包含算術表達式


           

          十一、子查詢

          (1)如果底層數據庫支持子查詢,則可以在HQL語句中使用子查詢。如下:

          from Cat as fatcat where fatcat.weight > (select avg(cat.weight) from DomesticCat cat)

          (2)如果子查詢是多行結果集,則應該使用多行運算符。如下:

          from Cat as cat where not(cat.name, cat.color) in (select cat.name, cat.color from DomesticCat cat)

          (3)SQL語法中子查詢還可以出現在select子句之后,HQL也支持這種用法,看如下HQL語句:

          select cat.id,(select max(kit.weight) from cat.kitten kit)

          from Cat as cat

          注意:HQL子查詢只可以在select子句或者where子句中出現。

           

          十二、命名查詢

          HQL支持將查詢所用的HQL語句放入配置文件中,而不是代碼中。

          在Hibernate映射文件的<hibernate-mapping/>元素中使用<query/>子元素來定義命名查詢,使用<query/>元素只需要指定一個name屬性,指定該命名查詢的名字。該元素的內容就是命名查詢的HQL語句。如下配置文件片段:

          <query name="myNameQuery">

                  from Person as p where p.age > ?

          </query>

          配置好后,通過Session提供的一個getNameQuery(String name)方法,該方法用于創建一個Query對象,剩下的操作與普通HQL完全一樣。如下所示:

          List p1 = sess.getNamedQuery("myNameQuery").setInteger(0,20).list()


           

          十三、條件查詢

          條件查詢通過如下3個類完成:

          Criteria:代表一次查詢。

          Criterion:代表一個查詢條件。

          Restrictions:產生查詢條件的工具類。

          執行條件查詢的步驟如下:

          (1)獲得Hibernate的Session對象。

          (2)以Session對象創建Criteria對象。

          (3)使用Restrictions的靜態方法創建Criterion查詢條件。

          (4)向Criteria查詢中體檢Criterion查詢條件。

          (5)執行Criteria的list等方法返回結果集。

          代碼片段如下:

          List l = session.createCriteria(Student.class)

                      //此處增加限制條件必須是Student已經存在的屬性

                       .add(Restrictions.get("studentNumber", 20050231L))

                      //如果要增加對Student的關聯類的屬性的限制

                      //則必須重新createCriteria()

                      //如果此關聯屬性是集合,則只要集合里任意一個對象的屬性滿足下面條件即可

                      .createCriteria("enrolments")

                      .add(Restrictions.gt("semester",2))

                      .list();

          在條件查詢中,Criteria接口代表依次查詢,該查詢本身不具備任何的數據篩選功能,Session調用createCriterial(Class clazz)方法對某個持久化類創建條件查詢實例。

          Criteria對象不具備任何的數據篩選功能,但程序可以通過向Criteria對象中組合多個Criterion(每個Criterion對象代表一個過濾條件)即可實現數據過濾了。

          Criterion接口代表一個查詢條件,該查詢條件由Restrictions負責產生。Restrictions是專門用于產生查詢條件的工具類,它的方法大部分都是靜態方法,常用的方法如下:

          (1)static Criteion allEq(Map propertyNameValues):判斷指定屬性(由Map參數的key指定)和指定值(由Map參數的value指定)是否完全相等。

          (2)static Criterion between(String propertyName, Object lo, Object hi):判斷屬性值在某個指范圍之內。


           

          十四、關聯和動態關聯

          代碼片段如下:

          List l = session.createCriteria(Student.class)

                      //此處增加限制條件必須是Student已經存在的屬性

                       .add(Restrictions.get("studentNumber", 20050231L))

                      //如果要增加對Student的關聯類的屬性的限制

                      //則必須重新createCriteria()

                      //如果此關聯屬性是集合,則只要集合里任意一個對象的屬性滿足下面條件即可

                      .createCriteria("enrolments")

                      .add(Restrictions.gt("semester",2))

                      .list();

          上面的代碼表示建立Person類的條件查詢,第一個查詢條件是直接過濾Person的屬性。第二個查詢條件則過濾Person的關聯實體的屬性,其中enrolments是Person類的關聯實體,而semester則是Enrolment類的屬性。值得注意的是,返回的并不是Enrolment對象,而是Person對象的集合。

          注意:使用關聯類的條件查詢,依然是查詢原有持久化類的實例,而不是查詢被關聯類的實例。

          可使用條件查詢支持的替換形態,將上面查詢代碼替換成如下形式:

          List l = session.createCriteria(Student.class)

                      .add(Restrictions.gt("studentNumber",20050231L))

                      .createAlias("enrolments","en")

                      .add(Restrictions.gt("semester",2))

                       .list();

          createAlias()方法并不創建一個新的Criteria實例,它只是給關聯實體(包含集合里包含的關聯實體)起一個別名,讓后面過濾條件可根據關聯實體進行篩選。

          在默認情況下,條件查詢將根據映射文件指定的延遲加載策略來加載關聯實體,如果希望在條件查詢中改變延遲加載策略(就像在HQL查詢中使用fetch關鍵字一樣),那就可通過Creteria的setFetchMode()方法來實現,該方法接受一個FetchMode參數。

          FetchMode里有幾個常量,如下:

          DEFAULT:使用配置文件制定的延遲加載策略處理。

          JOIN:使用外連接,預初始化所有關聯實體。

          SELECT:啟用延遲加載,系統將使用單獨的select語句來初始化關聯實體。只有當個真正訪問關聯實體的時候,才會執行第二條select語句。

          初始化Student對象時,也可以初始化Student關聯的Enrolment實體,實體使用如下代碼:
          List l = session.createCriteria(Student.class)

                      .add(Restrictions.gt("studentNumber",20050231L))

                      .setFetchMode("enrolments", FetchMode.JOIN)

                      .list();


           

          十五、投影、聚合和分組

          投影運算實際上就是一個基于列的運算,通常用于投影到指定列(也就是過濾其他列,類似select子句的作用),還可以完成SQL語句中常用的分組、組篩選等功能。

          Hibernate的條件過濾中使用Projection代表投影運算,Projection是一個接口,而Projections作為Projection的工廠,負責生成Projection對象。

          一旦產生了Projection對象之后,就可通過Criteria提供的setProjection(Projection projection)方法來進行投影運算。從該方法上看,每個Criteria只能接受一個投影運算,似乎無法進行多個投影運算,但實際上Hibernate又提供了一個ProjectionList類,該類是Projection的子類,并可以包含多個投影運算,通過這種方式即完成多個投影運算。

          因此,一個條件查詢的投影運算通常有如下程序結構:

          List cats = session.createCriteria(Cat.class)

                           .setProjection(Projections.projectionList()

                           .add(Projections.rowCount())

                           .add(Projections.avg("weight"))

                           .add(Projections.groupProperty("color"))

                            ).addOrder(Order.asc("color"))

                            .list();

          注意:使用條件查詢的投影運算時,不能使用顯示的分組子句,但某些投影類型的實質就是分組投影,這些投影元素將出現在SQL的group by子句中——如上的groupProperty("color")投影。

          投影運算的實質和group by子句、聚集函數的功能大致一致。

          除此之外,如果我們希望對分組(投影)后屬性進行排序,那就需要為投影運算指定一個別名。為投影運算指定別名有3種方法:

          (1)使用Projections的alias()方法為指定投影指定別名。一旦為projection指定了別名,則程序就可以根據該Projection別名來進行其他額外操作了,比如排序。條件查詢片段如下:

          List l = session.createCriteria(Enrolment.class)

                     .setProjection(Projections.projectionList()

                      //按course進行分組

                     .add(Projections.groupProperty("course"))

                     //統計記錄條數,并為統計結果指定別名c

                     .add(Projections.alias(Projections.rowCount()," c"))

                       ).addOrder(Order, asc("c"))

                     .list();

          (2)使用SimpleProjection的as()方法為自身指定別名。

          List l = session.createCriteria(Enrolment.class)

                     .setProjection(Projections.projectionList()

                      //按course進行分組

                     .add(Projections.groupProperty("course").as("c"))

                     //統計記錄條數,并為統計結果指定別名c

                     .add(Projections.rowCount())

                       ).addOrder(Order, asc("c"))

                     .list();

          (3)使用ProjectionList的add()方法添加投影時指定別名。

          ProjectionList的add()方法有兩個重載形式,一個是直接添加投影,另一個是在添加投影時指定別名。條件查詢片段如下:

          List l = session.createCriteria(Enrolment.class)

                     .setProjection(Projections.projectionList()

                      //按course進行分組,指定別名為c

                     .add(Projections.groupProperty("course"),("c"))

                     //統計記錄條數,并為統計結果指定別名rc

                     .add(Projections.rowCount(),"rc")

                       ).addOrder(Order, asc("c"))

                     .list();

          除此之外,Hibernate還提供了Property執行投影運算,Property投影的作用類似于SQL語句中的select,條件查詢的結果只有被Property投影的列才會被選出,條件查詢片段如下:

          List l = session.createCriteria(Student.class)

                      .setProjection(Property.forName("name"))

                      .list();

          上面條件查詢執行的結果不再是Student對象集合,而是name屬性所組成的集合。


           

          十六、離線查詢和子查詢

          條件查詢的離線查詢由DetachedCriteria來代表,DetachedCriteria類使你在一個session范圍之外創建一個查詢,并且可以使用任意的Session來執行它。

          DetachedCriteria還可代表子查詢,當我們把DetachedCriteria傳入Criteria中作為查詢條件時,DetachedCriteria就變成了子查詢。以下代碼片段所示:

          //定義一個離線查詢

          DetachedCriteria query = DetachedCriteria.forClass(Student.class)

                                                   .forClass(Student.class)

                                                   .setProjection(Property.forName("name"));

          //打開Session和事務(省略)

          //執行離線查詢

          List l = query.getExecutableCriteria(session).list();

          //執行子查詢

          List l = session.createCriteria(Student.class)

                     .add(Property.forName("name").in(query))

                     .list();

          如果程序使用Session的getExecutableCriteria()方法來執行DetachedCriteria對象,則它被當成離線查詢使用;如果程序使用Property的系列方法來操作DetachedCriteria對象,則它被當成子查詢使用。


           

          十七、事務和Session

          1、Session和用戶請求是一對一得關系,這是一種理想的Session管理模式。

          為了達到這種效果,推薦使用一個ThreadLocal變量,把Session綁定到處理客戶端請求的線程上。這種方式可以讓運行在該線程上的所有程序代碼輕松地訪問Session。也可以在一個ThreadLocal變量中保持事務上下文環境,不過這依賴于你所選擇的數據庫事務劃分機制。這種實現模式被稱為ThreadLocal Session和Open Session in View。

          工具類代碼如下:
          public class HibernateUtil

          {

                 //使用一個final變量來保存不可變的SessionFactory

                 public static final SessionFactory sessionFactory;

          static{

                   try{

                       //采用默認的Hibernate.cfg,xml來啟動一個Configuration的實例

                       Configuration conf = new Configuration().configure();

                       //由conf實例創建一個SessionFactory實例

                       sessionFactory = conf.buildSessionFactory();

                       }catch(Throwable ex){

                               System.err.println("初始化SessionFactory出現異常:"+ex);

                               throw new ExceptionInitializerError(ex);

                      }

                   }

          //ThreadLocal是隔離多個線程的數據共享

          //不存在多個線程之間共享資源,因此不再需要對線程同步

                    public static final ThreadLocal session = new ThreadLocal();

                    public static Session currentSession() throws HibernateException

                    {

                          Session s = (Session)session.get();

                          //如果該線程還沒有Session,則創建一個新的Session

                          if(s == null)

                          {

                              s = sessionFactory.openSession();

                          //將獲得的Session變量存儲在ThreadLocal變量session里

                               session.set(s);

                           }

                           return s;

                     }

                     public static void closeSession() throws HibernateException

                     {

                          Session s = (Session)session.get();

                          if(s != null)

                          {

                                 s.close();

                                 session.set(null);

                          }

                     }

          }

          在上面的代碼中,Hibernate Session被綁定到當前線程。當調用currentSession方法時,如果當前線程中的Session已經創建出來,那么將返回這個已經存在的Session實例。

          注意:幾乎所有情況下,都不要使用每個應用對應一次Hibernate Session的模式,也不要使用每次Http Session對應一次Hibernate Session的模式。

          2、對于以上的情況,Hibernate主要有如下3種模式來解決這個問題:

          (1)自動版本化:Hibernate能夠自動進行樂觀并發控制,如果在用戶思考的過程中持久化實體發生并發修改,Hibernate能夠自動檢測到。

          (2)脫管現象:如果采用每次用戶請求對應一次Session的模式,那么,前面載入的實例在用戶思考的過程中,始終與Session脫離,處于脫管狀態。Hibernate允許把脫管對象重新關聯到Session上,并且對修改進行持久化。在這種模式下,自動版本化被用來隔離并發修改。這種模式也被稱為脫管對象的每次請求對應一個Hibernate Session。

          (3)長生命周期Session:Session可以在數據庫事務提交之后,斷開和底層的JDBC連接。當新的客戶端請求到來時,它又重新連接上底層的JDBC鏈接。這種模式被稱為每個應用程序事務對應一個Session。因為應用程序事務時相當長(跨越多個用戶請求)的,所以也被稱為長生命周期Session。

          注意:Session緩存了處于持久化狀態的每個對象(Hibernate會監視和檢查臟數據),也就是說,如果程序讓Session打開很長一段時間,或者載入了很多數據,Session占用的內存會一直增長,直到拋出OutOfMemoryException異常。為了解決這個問題,程序定期調用Session的clear()和evict()方法來管理Session的緩存。對于一些大批量的數據處理,推薦使用DML風格的HQL語句完成。

          3、為了保證在Session關閉之前初始化代理屬性或集合屬性,程序可以Hibernate,initialized()靜態方法來強制初始化集合或代理。只要Session處于open狀態,Hibernate.initialize(teahcer)將會強制初始化teacher代理,Hibernate.initialize(teacher.getStudents())對student的集合具有同樣的功能。

          還有另一種選擇,就是程序讓Session一直處于打開狀態,直到裝入所有需要的集合或代理。

          保證Session處于打開狀態有兩種方法可以解決此問題:

          (1)在一個Web應用中,可以利用過濾器(Filter),在用戶請求結束、頁面生成結束時關閉Session。也就是保證在視圖層一直打開Session,這就是所謂的Open Session in View成視圖界面的過程中發生異常時,必須保證可以正確關閉Session,并結束事務。

          (2)使用業務邏輯層來準備數據,在業務邏輯層返回數據之前,業務邏輯層對每個所需集合調用Hibernate.initialize()方法,或者使用帶fetch子句或FetchMethod.JOIN的查詢,事先取得所有數據,并將這些數據封裝成VO(值對象)集合,然后程序可以關閉Session了。業務邏輯層VO集傳入視圖層,讓視圖層只負責簡單的顯示邏輯。在這種模式下,可以讓視圖層和Hibernate API徹底分離,保證視圖層不會出現持久層API,從而提供更好的解耦。


           

          十七、上下文相關的Session

          從Hibernate3.1開始,SessionFactory.getCurrentSession()的底層實現是可插拔的,Hibernate引入了CurrentSessionContext接口,并通過hibernate_current_session_context_class參數來管理上下文相關Session的底層實現。

          CurrentSessionContext接口有如下3個實現類:

          org.hibernate.context.JTASessionContext:根據JTA來跟蹤和界定上下文相關Session,這和最早的僅支持JTA的方法是完全一樣的。

          org.hibernate.context.ThreadLocalSessionContext:通過當前正在執行的線程來跟蹤和界定上下文相關Session,也就是和前面的HibernateUtil的Session維護模式相似。

          org.hibernate.context.ManagedSessionContext:通過當前執行的線程來跟蹤和界定上下文相關的Session。但是程序需要使用這個類的靜態方法將Session實例綁定、取消綁定,它并不會自動打開、flush或者關閉任何Session。

          對于容器中使用Hibernate的場景而言,通常會采用第一種方式:對于獨立的Hibernate應用而言,通常會采用第二種方式。

          為了指定Hibernate使用哪種Session管理方式,可以在hibernate.cfg.xml文件中增加如下片段:

          <property name="hibernate.current_session_context_class">thread</property>

          如果在JTA事務環境中,則應增加如下配置片段:

          <property name="hibernate.current_session_context_class">jta</property>

          對于第三種不常用的Session管理機制,則可在配置文件中簡寫成:managed。

          posted on 2011-11-19 14:05 小熊寶貝的每一天 閱讀(4547) 評論(1)  編輯  收藏

          評論:
          # re: 11.19之HQL----用于獲取對象的。 2013-07-07 21:18 | 拍磚
          終于看完了, 自己也跟著翹了一遍, 受益匪淺, 非常感謝。  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 安达市| 盐池县| 巴林右旗| 麻栗坡县| 前郭尔| 孝感市| 华容县| 临安市| 新野县| 樟树市| 诸暨市| 东平县| 瑞金市| 缙云县| 城市| 开江县| 宁都县| 宜兰县| 梁平县| 准格尔旗| 工布江达县| 宣武区| 广元市| 德兴市| 乳源| 五大连池市| 泰来县| 鸡西市| 昭平县| 平利县| 鄂伦春自治旗| 安庆市| 郧西县| 伊宁县| 凉城县| 安徽省| 抚州市| 泗阳县| 偏关县| 壶关县| 化隆|