在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的解決n+1的方法
hibernate的檢索策略包括類級別檢索策略和關聯級別檢索策略。
類級別檢索策略有立即檢索和延遲檢索,預設的檢索策略是立即檢索。在hibernate對映檔案中,通過在上配置 lazy屬性來確定檢索策略。對於session的檢索方式,類級別檢索策略僅適用於load方法;也就說,對於get、qurey檢索,持久化物件都會被立即載入而不管lazy是false還是true.一般來說,我們檢索物件就是要訪問它,因此立即檢索是通常的選擇。由於load方法在檢索不到物件時會丟擲異常(立即檢索的情況下),因此我個人並不建議使用load檢索;而由於中的lazy屬性還影響到多對一及一對一的檢索策略,因此使用load方法就更沒必要了。
關聯級別檢索策略有立即檢索、延遲檢索和迫切左外連線檢索。對於關聯級別檢索,又可分為一對多和多對多、多對一和一對一兩種情況討論。
1)立即檢索:這是一對多預設的檢索策略,此時lazy=false,outer-join=false.儘管這是預設的檢索策略,但如果關聯的集合是無用的,那麼就不要使用這種檢索方式。
2)延遲檢索:此時lazy=true,outer-join=false(outer-join=true是無意義的),這是優先考慮的檢索方式。
3)迫切左外連線檢索:此時 lazy=false,outer-join=true,這種檢索策略只適用於依靠id檢索方式(load、get),而不適用於query的集合檢索(它會採用立即檢索策略)。相比於立即檢索,這種檢索策略減少了一條sql語句,但在hibernate中,只能有乙個配置成 outer-join=true.
多對一和一對一檢索策略一般使用、配置。中需要配置的屬性是 outer-join,同時還需要配置one端關聯的的lazy屬性(配置的可不是中的lazy哦),它們的組合後的檢索策略如下:
1) outer-join=auto:這是預設值,如果lazy=true為延遲檢索,如果lazy=false為迫切左外連線檢索
2) outer-join=true,無關於lazy,都為迫切左外連線檢索。
3) outer-join=false,如果lazy=true為延遲檢索,否則為立即檢索。
可以看到,在預設的情況下(outer-join=auto,lazy=false),對關聯的one端物件hibernate採用的迫切左外連線檢索。依我看,很多情況下,我們並不需要載入one端關聯的物件(很可能我們需要的僅僅是關聯物件的id);另外,如果關聯物件也採用了迫切左外連線檢索,就會出現select語句中有多個外連線表,如果個數多的話會影響檢索效能,這也是為什麼hibernate通過 hibernate.max_fetch_depth屬性來控制外連線的深度。對於迫切左外連線檢索,query的集合檢索並不適用,它會採用立即檢索策略。
對於檢索策略,需要根據實際情況進行選擇。對於立即檢索和延遲檢索,它們的優點在於select語句簡單(每張表一條語句)、查詢速度快,缺點在於關聯表時需要多條select語句,增加了訪問資料庫的頻率。因此在選擇即檢索和延遲檢索時,可以考慮使用批量檢索策略來減少select語句的數量(配置batch-size屬性)。對於切左外連線檢索,優點在於select較少,但缺點是select語句的複雜度提高,多表之間的關聯會是很耗時的操作。另外,配置檔案是死的,但程式是活的,可以根據需要在程式裡顯示的指定檢索策略(可能經常需要在程式中顯示指定迫切左外連線檢索)。為了清楚檢索策略的配置效果如何,可以配置show_sql屬性檢視程式執行時hibernate執行的sql語句。
如何解決查詢N 1問題
1.使用資料庫left join來實現,在一次資料庫查詢中查出多條資料,但是要對結果進行分組組裝。但是對於分頁支援不好,需要自定義分頁外掛程式,現有解決思路如下 編寫sql時,將主表的條件寫入到where條件中,將所有left join的條件寫到on中,使用and來拼接多個條件,然後編寫分頁外掛程式...
JPA 解決n 1次查詢問題
分兩步操作 1 主表實體類中,新增註解 namedentitygraph 如上,設定name值,並指定attributenodes 看名字就知道可以指定多個 每個node的值如下圖紅框 2 在主表的dao方法中 重寫用到的查詢方法,service層中呼叫什麼方法就重寫什麼方法,我這邊用到的是 pag...
Hibernate解決n 1問題
觀點 對於n 1問題的理解。一般而言說n 1意思是,無論在一對多還是多對一當查詢出n條資料之後,每條資料會關聯的查詢1次他的關聯物件,這就叫做n 1。但是我的理解是,本來所有資訊可以一次性查詢出來,也就是簡單的連表查詢,但是hibernate會首先查詢1次得到當前物件,然後當前物件裡面的n個關聯物件...