hibernate 查詢極慢,最後查出是N 1問題

2021-06-19 12:50:33 字數 2749 閱讀 1973

兩三周之前遇到的乙個問題,終於在今天解決,感覺心情舒暢啊!

hibernate查詢資料庫時,不知道什麼原因變得非常的慢,但是同樣的**在另外的伺服器上卻一點問題也不慢。一直以為是記憶體的原因,因為生產用的伺服器的記憶體不記得是32g還是64g的了,自己的電腦是2g而且還是遠端操作,再加上要做其他任務開發,所以一直沒有踏實地去解決這個問題。過去的兩周裡都是在刷頁面,開啟乙個主要頁面要等20分鐘左右,有時候要多登入不同的使用者進行測試則需要等上1個小時,如果測試不通過,又得再等乙個小時,這開發效率極慢,所以只好加班加點地把開發任務做完。

近兩天,終於做完開發任務,便開始主攻這個hibernate查詢緩慢的問題。

關於這個問題,諮詢過很多同事,以及it行業的同學朋友

主要朝下面幾個方向去思考解決:

1,sql調優,建索引

2,**是否設計合理

3,檢視log日誌檔案,hibernate查詢語句的輸出

4,n+1問題

因為半路出家進入程式猿行業,所以前面的1、2未作為第一選擇,3也檢視了發現有許多的select語句,後確定是n+1問題。

介紹n+1問題

如果當sql資料庫中select語句數目過多,就會影響資料庫的效能,如果需要查詢n個customer物件,那麼必須執行n+1次select查詢語句,下文就將為您講解這個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。

(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提供了兩種檢索策略:延遲檢索策略和迫切左外連線檢索策略

1、延遲檢索策略能避免多餘載入應用程式不需要訪問的關聯物件,

hibernate3開始已經預設是lazy=true了;lazy=true時不會立刻查詢關聯物件,只有當需要關聯物件(訪問其屬性)時才會發生查詢動作。

2、迫切左外連線檢索策略則充分利用了sql的外連線查詢功能,能夠減少select語句的數目。

可以在對映檔案中定義連線抓取方式。

或者使用hql的left outer join.

或者在條件查詢中使用setfetchmode(fetchmode.join)

customer ctm = (customer)session.createcriteria(customer.class)

.setfetchmode(「order」.join)

.add(restrictions.ideq(customer_id));

因為關聯到的表比較多近20張,一開始胡亂地給所有的set都新增 fetch="join"  檢視相關的抓取策略  又修改了相關表的lazy屬性,導致出現許多問題,如

等等後靜下心,根據hibernate列印出來的select語句,只修改部分資料表的fetch,問題解決,也不報錯。

Hibernate 查詢方式

hibernate共有三種查詢方式 hql qbc和sql hql寫起來靈活直觀,而且與所熟悉的sql的語法類似。條件查詢 分頁查詢 連線查詢 巢狀查詢,包括一些查詢函式 count sum 等 查詢條件的設定等寫起來與sql語法一致,主要區別就是把表名換成了類或者物件。注意 在hql中關鍵字不區分...

Hibernate模糊查詢

hibernate模糊查詢 和sql查詢一樣,hibernate,hql使用like關鍵字進行模糊查詢。模糊查詢能夠比較字串是否與指定的字串模式匹配。其中使用萬用字元表示 如下 百分號 匹配任意型別 任意長度的字串,中文則需要兩個百分號 下劃線 匹配單個任意字元,一般用來限制字串表示式的長度。下面舉...

hibernate 投影查詢

1.投影查詢就是想查詢某一字段的值或者某幾個欄位的值 2.投影查詢的案例 如果查詢多個字段,例如下面這種方式 listlist session.createquery select c.cust name,c.cust level from customer c list for object ob...