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

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

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

          例如:

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

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

          from Person as p

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

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

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

          二、關(guān)聯(lián)和連接

                 Hibernate使用關(guān)聯(lián)映射來處理底層數(shù)據(jù)表之間的連接,一旦我們提供了正確的關(guān)聯(lián)映射后,當(dāng)程序通過Hibernate進(jìn)行持久化訪問時,將可利用Hibernate的關(guān)聯(lián)來進(jìn)行連接。

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

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

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

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

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

          //使用顯示連接

          from Person p

          inner join p.myEvent event

          where event.happenDate < :endDate

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

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

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

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

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

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

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

          from Person p

          inner join p.myEvent event

          with p.id > event.id

          where event.happenDate < :endDate

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

                由于表連接的方式都是基于底層SQL來實現(xiàn)的,如果底層不支持這些外連接,那么執(zhí)行對應(yīng)的HQL時就會相應(yīng)地引發(fā)異常。

           

          三、隱式連接和顯示連接有如下兩點(diǎn)區(qū)別:

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

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

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

          當(dāng)HQL語句中省略select關(guān)鍵字時,使用顯示連接查詢返回的結(jié)果也是集合,但集合元素是被查詢持久化對象、所有被關(guān)聯(lián)的持久化對象所組成的數(shù)組。

           注意:

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

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

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

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

          使用fetch關(guān)鍵字時,有如下幾個注意點(diǎn):

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

                 fetch不能與獨(dú)立的with條件一起使用。

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

                 對bag映射而言,同時join fetch多個集合時可能出現(xiàn)非預(yù)期結(jié)果,因此需要謹(jǐn)慎使用。

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

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

          from Document fetch all properties order by name


          四、HQL查詢的select子句

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

          例如:

          select p.name from Person as p

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

          select p.name.firstName from Person as p

          (3)在特殊情況下,如果select后只有一項(包括持久化實例或?qū)傩裕瑒t查詢得到的集合元素就是該持久化實例或?qū)傩浴?/p>

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

          例如:

          select p.name,p from Person as p

          執(zhí)行該HQL語句得到的集合元素時類似于[String,Person]結(jié)構(gòu)的數(shù)組,其中第一個元素時Person實例的name屬性,第二個元素時Person實例。

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

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

          例如:

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

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

          例如:

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

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

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

          select p.name as personName from Person as p

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

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

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


           

          五、HQL查詢的聚集函數(shù)

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

          例如,如下的HQL語句:

          select count(*) from Person

          select max(p.page) from Person as p

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

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

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


           

          六、多態(tài)查詢

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

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

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

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

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


           

          七、HQL查詢的where子句

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

          如下面兩條HQL語句:

          from Person where name like "tom%"

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

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

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

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

          select * from cat_table as table1 cat_table as table2

          where table1.mate = table2.mate

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

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

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

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

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

          //執(zhí)行多態(tài)查詢時,默認(rèn)會選出Cat及其所有子類的實例

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

          from Cat cat where cat.class = DomesticCat

          (4)當(dāng)where子句中的運(yùn)算符只支持基本類型或者字符串時,where子句中的屬性表達(dá)式必須以基本類型或者字符串結(jié)尾,不要使用組件類型屬性結(jié)尾,例如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%"


           

          八、表達(dá)式

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

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

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

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

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

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

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

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

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

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

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

          2、如果在Hibernate配置文件中進(jìn)行如下聲明:

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

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

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

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

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

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

          5、結(jié)構(gòu)變量:size、elements、indices等,只能在where子句中使用。


           

          九、order by子句

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

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

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

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

          如果沒有指定排序規(guī)則,默認(rèn)采用升序規(guī)則。

          十、group by子句

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

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

          其規(guī)則類似于SQL規(guī)則。

          having子句用于對分組進(jìn)行過濾,如下所示:

          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 子句中都不能包含算術(shù)表達(dá)式


           

          十一、子查詢

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

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

          (2)如果子查詢是多行結(jié)果集,則應(yīng)該使用多行運(yùn)算符。如下:

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

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

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

          from Cat as cat

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

           

          十二、命名查詢

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

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

          <query name="myNameQuery">

                  from Person as p where p.age > ?

          </query>

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

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


           

          十三、條件查詢

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

          Criteria:代表一次查詢。

          Criterion:代表一個查詢條件。

          Restrictions:產(chǎn)生查詢條件的工具類。

          執(zhí)行條件查詢的步驟如下:

          (1)獲得Hibernate的Session對象。

          (2)以Session對象創(chuàng)建Criteria對象。

          (3)使用Restrictions的靜態(tài)方法創(chuàng)建Criterion查詢條件。

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

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

          代碼片段如下:

          List l = session.createCriteria(Student.class)

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

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

                      //如果要增加對Student的關(guān)聯(lián)類的屬性的限制

                      //則必須重新createCriteria()

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

                      .createCriteria("enrolments")

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

                      .list();

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

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

          Criterion接口代表一個查詢條件,該查詢條件由Restrictions負(fù)責(zé)產(chǎn)生。Restrictions是專門用于產(chǎn)生查詢條件的工具類,它的方法大部分都是靜態(tài)方法,常用的方法如下:

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

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


           

          十四、關(guān)聯(lián)和動態(tài)關(guān)聯(lián)

          代碼片段如下:

          List l = session.createCriteria(Student.class)

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

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

                      //如果要增加對Student的關(guān)聯(lián)類的屬性的限制

                      //則必須重新createCriteria()

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

                      .createCriteria("enrolments")

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

                      .list();

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

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

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

          List l = session.createCriteria(Student.class)

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

                      .createAlias("enrolments","en")

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

                       .list();

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

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

          FetchMode里有幾個常量,如下:

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

          JOIN:使用外連接,預(yù)初始化所有關(guān)聯(lián)實體。

          SELECT:啟用延遲加載,系統(tǒng)將使用單獨(dú)的select語句來初始化關(guān)聯(lián)實體。只有當(dāng)個真正訪問關(guān)聯(lián)實體的時候,才會執(zhí)行第二條select語句。

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

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

                      .setFetchMode("enrolments", FetchMode.JOIN)

                      .list();


           

          十五、投影、聚合和分組

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

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

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

          因此,一個條件查詢的投影運(yùn)算通常有如下程序結(jié)構(gòu):

          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();

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

          投影運(yùn)算的實質(zhì)和group by子句、聚集函數(shù)的功能大致一致。

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

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

          List l = session.createCriteria(Enrolment.class)

                     .setProjection(Projections.projectionList()

                      //按course進(jìn)行分組

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

                     //統(tǒng)計記錄條數(shù),并為統(tǒng)計結(jié)果指定別名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進(jìn)行分組

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

                     //統(tǒng)計記錄條數(shù),并為統(tǒng)計結(jié)果指定別名c

                     .add(Projections.rowCount())

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

                     .list();

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

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

          List l = session.createCriteria(Enrolment.class)

                     .setProjection(Projections.projectionList()

                      //按course進(jìn)行分組,指定別名為c

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

                     //統(tǒng)計記錄條數(shù),并為統(tǒng)計結(jié)果指定別名rc

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

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

                     .list();

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

          List l = session.createCriteria(Student.class)

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

                      .list();

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


           

          十六、離線查詢和子查詢

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

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

          //定義一個離線查詢

          DetachedCriteria query = DetachedCriteria.forClass(Student.class)

                                                   .forClass(Student.class)

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

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

          //執(zhí)行離線查詢

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

          //執(zhí)行子查詢

          List l = session.createCriteria(Student.class)

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

                     .list();

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


           

          十七、事務(wù)和Session

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

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

          工具類代碼如下:
          public class HibernateUtil

          {

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

                 public static final SessionFactory sessionFactory;

          static{

                   try{

                       //采用默認(rèn)的Hibernate.cfg,xml來啟動一個Configuration的實例

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

                       //由conf實例創(chuàng)建一個SessionFactory實例

                       sessionFactory = conf.buildSessionFactory();

                       }catch(Throwable ex){

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

                               throw new ExceptionInitializerError(ex);

                      }

                   }

          //ThreadLocal是隔離多個線程的數(shù)據(jù)共享

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

                    public static final ThreadLocal session = new ThreadLocal();

                    public static Session currentSession() throws HibernateException

                    {

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

                          //如果該線程還沒有Session,則創(chuàng)建一個新的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被綁定到當(dāng)前線程。當(dāng)調(diào)用currentSession方法時,如果當(dāng)前線程中的Session已經(jīng)創(chuàng)建出來,那么將返回這個已經(jīng)存在的Session實例。

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

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

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

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

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

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

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

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

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

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

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


           

          十七、上下文相關(guān)的Session

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

          CurrentSessionContext接口有如下3個實現(xiàn)類:

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

          org.hibernate.context.ThreadLocalSessionContext:通過當(dāng)前正在執(zhí)行的線程來跟蹤和界定上下文相關(guān)Session,也就是和前面的HibernateUtil的Session維護(hù)模式相似。

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

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

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

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

          如果在JTA事務(wù)環(huán)境中,則應(yīng)增加如下配置片段:

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

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

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

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

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 河东区| 栖霞市| 且末县| 宁远县| 民权县| 滁州市| 祁连县| 五莲县| 时尚| 浪卡子县| 东海县| 鹤峰县| 新绛县| 潜山县| 射阳县| 景德镇市| 湖北省| 栾川县| 沁阳市| 连平县| 大竹县| 原阳县| 金山区| 文安县| 乃东县| 建始县| 绍兴县| 巴林左旗| 集贤县| 巨鹿县| 大渡口区| 康定县| 临汾市| 开江县| 阿拉善盟| 剑川县| 兴安盟| 于都县| 贵港市| 莫力| 乌鲁木齐县|