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