什麼叫n+1次select查詢問題?數據庫
在Session的緩存中存放的是相互關聯的對象圖。默認狀況下,當Hibernate從數據庫中加載Customer對象時,會同時加載全部關聯的Order對象。以Customer和Order類爲例,假定ORDERS表的CUSTOMER_ID外鍵容許爲null,圖1列出了CUSTOMERS表和ORDERS表中的記錄。
如下Session的find()方法用於到數據庫中檢索全部的Customer對象:
List customerLists=session.find("from Customer as c");
運行以上find()方法時,Hibernate將先查詢CUSTOMERS表中全部的記錄,而後根據每條記錄的ID,到ORDERS表中查詢有參照關係的記錄,Hibernate將依次執行如下select語句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;
經過以上5條select語句,Hibernate最後加載了4個Customer對象和5個Order對象,在內存中造成了一幅關聯的對象圖,參見圖2。數組
Hibernate在檢索與Customer關聯的Order對象時,使用了默認的當即檢索策略。這種檢索策略存在兩大不足:
(1) select語句的數目太多,須要頻繁的訪問數據庫,會影響檢索性能。若是須要查詢n個Customer對象,那麼必須執行n+1次select查詢語句。這就是經典的n+1次select查詢問題。這種檢索策略沒有利用SQL的鏈接查詢功能,例如以上5條select語句徹底能夠經過如下1條select語句來完成:
select * from CUSTOMERS left outer join ORDERS
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select語句使用了SQL的左外鏈接查詢功能,可以在一條select語句中查詢出CUSTOMERS表的全部記錄,以及匹配的ORDERS表的記錄。
(2)在應用邏輯只須要訪問Customer對象,而不須要訪問Order對象的場合,加載Order對象徹底是多餘的操做,這些多餘的Order對象白白浪費了許多內存空間。
爲了解決以上問題,Hibernate提供了其餘兩種檢索策略:延遲檢索策略和迫切左外鏈接檢索策略。延遲檢索策略能避免多餘加載應用程序不須要訪問的關聯對象,迫切左外鏈接檢索策略則充分利用了SQL的外鏈接查詢功能,可以減小select語句的數目。緩存
Hibernate檢索策略session
鏈接查詢:
關係型數據庫之因此強大,其中一個緣由就是能夠統一使用表來管理同類數據信息,而且能夠在相關數據之間創建關係。做爲支持關係型數據庫的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的各類版本中,只容許在一個查詢中迫切左外鏈接檢索一個集合,即只容許存在一個一對多關聯,可是容許存在多個一對一和多對多關聯。框架