Hibernate N+1 問題及解決辦法sql
問題出現的緣由:數據庫
Hibernate 中常會用到 set , bag 等集合表示 1 對多的關係,在獲取實體的時候就能根據關係將關聯的對象或者對象集取出,還能夠設定 cacade 進行關聯更新和刪除。這不得不說 hibernate 的 orm 作得很好,很貼近 oo的使用習慣了。緩存
可是對數據庫訪問仍是必須考慮性能問題的,在設定了 1 對多這種關係以後, 查詢就會出現傳說中的 n+1 問題。性能
一對多: 在一方,查找獲得了 n 個對象,那麼又須要將 n 個對象關聯的集合取出,因而原本的一條 sql 查詢變成了 n+1 條;fetch
多對一: 在多方,查詢獲得了 m 個對象,那麼也會將 m 個對象對應的 1 方的對象取出, 也變成了 m+1 ;spa
解決問題的方法:hibernate
一、 使用 fetch 抓取, Hibernate 抓取策略分爲單端代理和集合代理的抓取策略。代理
Hibernate 抓取策略 ( 單端代理的抓取策略 ) :orm
保持默認也就是以下 :對象
<many-to-one name="clazz" cascade="save-update" fetch="select" />
fetch="select" 就是另外發送一條 select 語句抓取當前對象關聯實體或者集合設置 fetch="join"
<many-to-one name="clazz" cascade="save-update" fetch="join"/>
Hibernate 會經過 select 語句使用外鏈接來加載器關聯實體活集合此時 lazy 會失效
Hibernate 抓取策略 ( 集合代理的抓取策略 ) :
保持默認( fetch="select" )也就是以下 :
<set name="students" inverse="true">
<key column="clazz"/>
<one-to-many class="com.june.hibernate.Student"/>
</set>
1)fetch="select" 會另外發出一條語句查詢集合
2) 設置 fetch="join" 採用外鏈接集合的 lazy 失效
3) 這隻 fetch="subselect" 另外發出一條 select 語句抓取前面查詢到的全部的實體對象的關聯集合 fetch只對 HQL 查詢產生影響其餘的則不會
二、 使用 map 直接搜索須要的列
如:產品 product 和產品分類 product_category 兩張表,多對一關係。查詢產品列表時
select new Map(p.id as id, p.name as name, p.category.name as categoryName) from Product p
對數據庫訪問仍是必須考慮性能問題的, 在設定了1 對多這種關係以後, 查詢就會出現傳說中的n +1 問題。
1 )1 對多,在1 方,查找獲得了n 個對象, 那麼又須要將n 個對象關聯的集合取出,因而原本的一條sql查詢變成了n +1 條
2)多對1 ,在多方,查詢獲得了m個對象,那麼也會將m個對象對應的1 方的對象取出, 也變成了m+1
怎麼解決n +1 問題?
1 )lazy=true, hibernate3開始已經默認是lazy=true了;lazy=true時不會馬上查詢關聯對象,只有當須要關聯對象(訪問其屬性,非id字段)時纔會發生查詢動做。
2)二級緩存, 在對象更新,刪除,添加相對於查詢要少得多時, 二級緩存的應用將不怕n +1 問題,由於即便第一次查詢很慢,以後直接緩存命中也是很快的。
不一樣解決方法,不一樣的思路,第二條卻恰好又利用了n +1 。
3) 固然你也能夠設定fetch=join(annotation : @ManyToOne() @Fetch(FetchMode.JOIN))