連接查詢:
關(guān)系型數(shù)據(jù)庫之所以強(qiáng)大,其中一個原因就是可以統(tǒng)一使用表來管理同類數(shù)據(jù)信息,并且可以在相關(guān)數(shù)據(jù)之間建立關(guān)系。作為支持關(guān)系型數(shù)據(jù)庫的SQL語句來說,自然要對全面發(fā)揮這種強(qiáng)大功能提供支持,這個支持就是連接查詢。同樣作為一種關(guān)系型數(shù)據(jù)庫的持久層框架,Hibernate也對連接查詢提供了豐富的支持,在Hibernate中通過HQL與QBC兩種查詢方式都可以支持連接查詢。下面這一部分我們將通過這兩種查詢技術(shù),來詳細(xì)討論有關(guān)Hibernate對連接查詢支持的各個細(xì)節(jié)。在講解連接查詢之前,我們先來回憶一下在第一部分中講解的有關(guān)實(shí)體關(guān)聯(lián)關(guān)系的映射,在實(shí)體的配置文件中可以通過配置集合元素來指定對關(guān)聯(lián)實(shí)體的映射以及檢索策略。(請參考第一部分相關(guān)內(nèi)容)因此我們可以在實(shí)體映射配置文件中,指定關(guān)聯(lián)實(shí)體檢索策略,對關(guān)聯(lián)實(shí)體的檢索策略可以指定為“延遲檢索”,“立即檢索”,“迫切左外連接檢索”,如下所示對與Customer實(shí)體關(guān)聯(lián)的Order實(shí)體設(shè)置延遲加載:<set name=”orders” inverse=”true” lazy=”true”>,這種在實(shí)體映射配置文件中設(shè)定的檢索策略,稱為默認(rèn)檢索策略,但是這種默認(rèn)檢索策略是可以被覆蓋的,那就是在程序代碼當(dāng)中可以動態(tài)指定各種迫切檢索策略來覆蓋默認(rèn)檢索策略。
1、 迫切左外連接查詢和左外連接查詢:
我們看以下代碼,這段代碼將覆蓋映射文件中的檢索策略,顯示指定采用迫切左外連接查詢。
HQL查詢方式:
Query query=session.createQuery(“from Customer c left join fetch c.orders o where c.name like ‘zhao%’ ”);
List list=query.list();
for(int i=0;i<list.size();i++){
Customer customer=(Customer)list.get(i);
}
//QBC檢索方式:
List list=session.createCriteria(Customer.class).setFetchMode(“orders”,FetchMode.EAGER)
.add(Expression.like(“name”,”zhao%”,MatchMode.START).list();
for(int i=0;i<list.size();i++){
Customer customer=(Customer)list.get(i);
}
我們看到在HQL以及QBC查詢中分別通過left join fetch和FetchMode.EAGER來指定采用迫切左外連接檢索策略,當(dāng)采用了迫切左外連接檢索策略時,當(dāng)進(jìn)行檢索時即執(zhí)行查詢的list()方法時,將會立即初始化用來容納關(guān)聯(lián)實(shí)體的集合對象元素,如果在實(shí)體映射配置文件中對關(guān)聯(lián)實(shí)體設(shè)置了延遲加載,那么此時將會忽略延遲加載設(shè)置,而采用迫切左外連接策略,并且立即用關(guān)聯(lián)實(shí)體對象填充集合對象元素,即使用Order對象填充Customer對象的orders集合。因此這種檢索策略會馬上創(chuàng)建關(guān)聯(lián)實(shí)體對象,此時我想你一定會想到這種檢索策略會同時檢索出Customer和Order實(shí)體對象對應(yīng)的數(shù)據(jù),并且分別創(chuàng)建這兩個對象。恭喜你答對了,因此上面代碼會生成類似如下的SQL語句:
Select * from customer c left join order o on c.id=o.id where c.name like ‘zhao%’;
如果我們忽略了fetch關(guān)鍵字,就變成了左外連接查詢,如下面代碼:
Query query=session.createQuery(“from Customer c left join c.orders o where c.name like ‘zhao%’ ”);
List list=query.list();
for(int i=0;i<list.size();i++){
Object[] objs=(Object[])list.get(i);
Customer customer=(Customer) objs[0];
Order order=(Order)objs[1];
}
我們可以看到采用左外連接查詢返回的結(jié)果集中包含的是對象數(shù)組,對象數(shù)組中的每個元素存放了一對相互關(guān)聯(lián)的Customer對象和Order對象,而迫切左外連接會返回Customer對象,與Customer對象相關(guān)聯(lián)的Order對象存放在Customer對象的集合元素對象中,這就是迫切左外連接和左外連接查詢的其中一個區(qū)別(這兩種檢索生成的SQL語句是一樣的),另一個區(qū)別是當(dāng)使用左外連接時,對關(guān)聯(lián)對象的檢索會依照實(shí)體映射配置文件所指定的策略,而不會像迫切左外連接那樣忽略它,比如此時對Customer對象關(guān)聯(lián)的Order對象采用延遲加載,那么左外連接檢索也會使用延遲加載機(jī)制檢索Order對象。
2、內(nèi)連接,迫切內(nèi)連接以及隱式內(nèi)連接:
若采用迫切內(nèi)連接通過一下代碼可以實(shí)現(xiàn):
Query query=session.createQuery(“from Customer c inner join fetch c.orders o where c.name like ‘zhao%’ ”);
List list=query.list();
for(int i=0;i<list.size();i++){
Customer customer=(Customer)list.get(i);
}
這段代碼將會采用迫切內(nèi)連接檢索,對集合元素的檢索策略以及返回結(jié)果集中的對象類型都采用與迫切左外連接一樣的方式,我這里就不再贅述,另外QBC查詢不支持迫切內(nèi)連接檢索。
如果去掉fetch就是內(nèi)連接檢索,如下面代碼:
Query query=session.createQuery(“from Customer c innerjoin c.orders o where c.name like ‘zhao%’ ”);
List list=query.list();
for(int i=0;i<list.size();i++){
Object[] objs=(Object[])list.get(i);
Customer customer=(Customer) objs[0];
Order order=(Order)objs[1];
}
內(nèi)連接檢索,對集合元素的檢索策略以及返回結(jié)果集中的對象類型都采用與左外連接一樣的方式,QBC查詢也同樣支持內(nèi)連接檢索,如下代碼:
List list=session.createCriteria(Customer.class)
.add(Expression.like(“name”,”zhao%”,MatchMode.START))
.createCriteria(“orders”)
.add(Expression.like(“ordernumber”,”T”,MatchMode.START)).list();
上面代碼等價于如下的HQL語句:
Select c from Customer c join c.orders o where c.name like ‘zhao%’ and o.ordernummber like ‘T%’;因此可以采用下面的方式訪問結(jié)果集:
for(int i=0;i<list.size();i++){
Customer customer=(Customer)list.get(i);
}
由此可見,采用內(nèi)連接查詢時,HQL與QBC查詢有不同的默認(rèn)行為,HQL會檢索出成對的Customer和Order對象,而QBC僅會檢索出Customer對象。如果QBC查詢想檢索出成對的Customer和Order對象,可以采用如下代碼:
List list=session.createCriteria(Customer.class)
.createAlias(“orders”,”o”)
.add(Expression.like(“this.name”,”zhao%”,MatchMode.START))
.add(Expression.like(“ordernumber”,”T”,MatchMode.START))
.returnMap()
.list();
for(int i=0;i<list.size();i++){
Map map=(Map)list.get(i);
Customer customer=(Customer)map.get(“this”);
Order order=(Order)map.get(“o”);
}
“o”和”this”分別是orders集合和Customer對象的別名。
在HQL查詢中,還有一種查詢成為隱式內(nèi)連接,我們看下面的HQL語句,
From Order o where o.customer.name like ’ zhao% ’;這個語句通過o.customer.name訪問與Order對象關(guān)聯(lián)的Customer對象的name屬性,盡管沒有使用join關(guān)鍵字,其實(shí)隱式指定了采用內(nèi)連接檢索,它和下面這條HQL語句等價:
From Order o join o.customer c where c.name like ‘zhao%’;
隱式內(nèi)連接只適用于多對一和一對一關(guān)聯(lián),不適用于一對多和多對多關(guān)聯(lián),另外QBC查詢不支持隱式內(nèi)連接檢索。
3、右外連接檢索:
由于fetch關(guān)鍵字只能應(yīng)用于innner join和left join,因此對于右外連接檢索而言,就不存在所謂的迫切右外連接查詢了,使用右外連接見如下代碼:
Query query=session.createQuery(“from Customer c right join c.orders o where c.name like ‘zhao%’ ”);
List list=query.list();
for(int i=0;i<list.size();i++){
Object[] objs=(Object[])list.get(i);
Customer customer=(Customer) objs[0];
Order order=(Order)objs[1];
}
右外連接檢索,對集合元素的檢索策略以及返回結(jié)果集中的對象類型都采用與左外連接一樣的方式。
4、交叉連接:
對于不存在關(guān)聯(lián)關(guān)系的兩個實(shí)體對象,不能使用內(nèi)連接查詢,也不能使用外連接查詢,此時可以使用具有SQL風(fēng)格的交叉連接,如下面代碼:
Select c.ID,c.name,c.age,o.ID,o.ordernumber,o.customer_ID
From Customer c,Order o;
這個HQL語句將會執(zhí)行交叉連接檢索,而且將會返回customer表和order表的笛卡兒積關(guān)聯(lián)結(jié)果。
5、連接查詢運(yùn)行時檢索策略總結(jié):
①、如果在HQL和QBC查詢中沒有指定檢索策略,那么將會使用映射配置為件中指定的檢索策略,但是這里有一個例外,那就是HQL檢索總是會忽略實(shí)體映射配置文件中對關(guān)聯(lián)實(shí)體指定的迫切左外連接檢索策略,也就是說如果配置文件中指定對關(guān)聯(lián)實(shí)體采用迫切走外連接檢索,但是在HQL查詢語句中沒有指定這種檢索策略,此時Hibernate將會忽略這種檢索策略,而依然采用立即檢索。因此如果希望采用迫切左外連接檢索,就必須在HQL語句中明確指定。
②、如果在HQL或者QBC檢索中明確指定了檢索策略,就會覆蓋配置文件中的默認(rèn)檢索策略,在HQL查詢中通過left join fetch和inner join fetch來明確指定檢索策略,在QBC查詢中通過FetchMode.DEFAULT,FetchMode.EAGER,FetchMode.LAZY來明確指定檢索策略。
①、 目前的Hibernate的各種版本中,只允許在一個查詢中迫切左外連接檢索一個集合,即只允許存在一個一對多關(guān)聯(lián),但是允許存在多個一對一和多對多關(guān)聯(lián)。
|
|