隨筆-59  評論-31  文章-0  trackbacks-0

          在Session的緩存中存放的是相互關(guān)聯(lián)的對象圖。默認情況下,當Hibernate從數(shù)據(jù)庫中加載Customer對象時,會同時加載所有關(guān)聯(lián)的 Order對象。以Customer和Order類為例,假定ORDERS表的CUSTOMER_ID外鍵允許為null

          以下Session的find()方法用于到數(shù)據(jù)庫中檢索所有的Customer對象: 

          List customerLists=session.find("from Customer as c"); 

          運行以上find()方法時,Hibernate將先查詢CUSTOMERS表中所有的記錄,然后根據(jù)每條記錄的ID,到ORDERS表中查詢有參照關(guān)系的記錄,Hibernate將依次執(zhí)行以下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對象,在內(nèi)存中形成了一幅關(guān)聯(lián)的對象圖.


          Hibernate在檢索與Customer關(guān)聯(lián)的Order對象時,使用了默認的立即檢索策略。這種檢索策略存在兩大不足: 

          (1) select語句的數(shù)目太多,需要頻繁的訪問數(shù)據(jù)庫,會影響檢索性能。如果需要查詢n個Customer對象,那么必須執(zhí)行n+1次select查詢語 句。這就是經(jīng)典的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)在應(yīng)用邏輯只需要訪問Customer對象,而不需要訪問Order對象的場合,加載Order對象完全是多余的操作,這些多余的Order對象白白浪費了許多內(nèi)存空間。 
          為了解決以上問題,Hibernate提供了其他兩種檢索策略:延遲檢索策略和迫切左外連接檢索策略。延遲檢索策略能避免多余加載應(yīng)用程序不需要訪問的關(guān)聯(lián)對象,迫切左外連接檢索策略則充分利用了SQL的外連接查詢功能,能夠減少select語句的數(shù)目。


          對數(shù)據(jù)庫訪問還是必須考慮性能問題的, 在設(shè)定了1 對多這種關(guān)系之后, 查詢就會出現(xiàn)傳說中的n +1 問題。 
          1 )1 對多,在1 方,查找得到了n 個對象, 那么又需要將n 個對象關(guān)聯(lián)的集合取出,于是本來的一條sql查詢變成了n +1 條 
          2)多對1 ,在多方,查詢得到了m個對象,那么也會將m個對象對應(yīng)的1 方的對象取出, 也變成了m+1

          怎么解決n +1 問題? 
          1 )lazy=true, hibernate3開始已經(jīng)默認是lazy=true了;lazy=true時不會立刻查詢關(guān)聯(lián)對象,只有當需要關(guān)聯(lián)對象(訪問其屬性,非id字段)時才會發(fā)生查詢動作。 

          2)二級緩存, 在對象更新,刪除,添加相對于查詢要少得多時, 二級緩存的應(yīng)用將不怕n +1 問題,因為即使第一次查詢很慢,之后直接緩存命中也是很快的。 
          不同解決方法,不同的思路,第二條卻剛好又利用了n +1 。

          3) 當然你也可以設(shè)定fetch=join(annotation : @ManyToOne() @Fetch(FetchMode.JOIN))

          posted on 2012-01-30 14:20 RoyPayne 閱讀(10912) 評論(1)  編輯  收藏 所屬分類: SSH

          評論:
          # re: Hibernate n+1問題 2012-12-25 18:11 | qinxike
          現(xiàn)在問題是加了fetch會報錯!除了fetch,沒有其他辦法了嗎?  回復  更多評論
            
          主站蜘蛛池模板: 上虞市| 长泰县| 雷波县| 福安市| 横山县| 西安市| 疏附县| 麻阳| 吴忠市| 砚山县| 贵德县| 五指山市| 兴山县| 望奎县| 合水县| 什邡市| 灵璧县| 临泽县| 漳平市| 阳江市| 梁山县| 塘沽区| 鲁山县| 隆德县| 木兰县| 南丹县| 马公市| 泰兴市| 灵璧县| 广水市| 桐柏县| 武穴市| 广州市| 南雄市| 南漳县| 讷河市| 徐水县| 紫阳县| 焦作市| 乌审旗| 边坝县|