hibernate中的N+1問題
什麼時候會遇到1+N的問題?
前提:Hibernate預設表與表的關聯方法是fetch="select",不是fetch="join",這都是為了懶載入而準備的。
1)一對多(<set><list>) ,在1的這方,通過1條sql查詢得到了1個物件,由於關聯的存在 ,那麼又需要將這個物件關聯的集合取出,所以合集數量是n還要發出n條sql,於是本來的1條sql查詢變成了1 +n條 。
2)多對一<many-to-one> ,在多的這方,通過1條sql查詢得到了n個物件,由於關聯的存在,也會將這n個物件對應的1 方的物件取出, 於是本來的1條sql查詢變成了1 +n條 。3)iterator 查詢時,一定先去快取中找(1條sql查集合,只查出ID),在沒命中時,會再按ID到庫中逐一查詢, 產生1+n條SQL
怎麼解決1+N 問題?
1 )lazy=true, hibernate3開始已經預設是lazy=true了;lazy=true時不會立刻查詢關聯物件,只有當需要關聯物件(訪問其屬性,非id欄位)時才會發生查詢動作。
2)使用二級快取, 二級快取的應用將不怕1+N 問題,因為即使第一次查詢很慢(未命中),以後查詢直接快取命中也是很快的。剛好又利用了1+N 。
3) 當然你也可以設定fetch="join",一次關聯表全查出來,但失去了懶載入的特性。
List和Iterator
a. list--從資料庫中查詢出所有的物件列表;只能利用查詢快取(但在交易系統中查詢快取作用不大),無法利用二級快取中的單個實體,但list查出的物件會寫入二級快取,但它一般只生成較少的執行SQL語句,很多情況就是一條(無關聯)。
load和get
Hibernate中有兩個極為相似的方法get()與load(),他們都可以通過指定的實體類與ID從資料庫中讀取資料,並返回對應的例項,但Hibernate不會搞兩個完全一樣的方法的,它們間的不同在於:
- 如果找不到符合條件的紀錄,get()方法將返回null.而load()將會報出ObjectNotFoundEcception.
- load()方法可以返回實體的代理類例項,而get()永遠只返回實體類.
- load()方法可以充分利用二級快取和內部快取的現有資料,而get()方法只在內部快取中進行查詢,如沒有發現對應資料將跳過二級快取,直接呼叫SQL完成查詢.
對於get方法,hibernate會確認一下該id對應的資料是否存在,首先在session快取中查詢,然後在二級快取中查詢,還沒有就查資料庫,資料庫中沒有就返回null。
對於第2點,雖然好多書中都這麼說:“get()永遠只返回實體類”,但實際上這是不正確的,get方法如果在session快取中找到了該id對應的物件,如果剛好該物件前面是被代理過的,如被load方法使用過,或者被其他關聯物件延遲載入過,那麼返回的還是原先的代理物件,而不是實體類物件,如果該代理物件還沒有載入實體資料(就是id以外的其他屬性資料),那麼它會查詢二級快取或者資料庫來載入資料,但是返回的還是代理物件,只不過已經載入了實體資料。
3。胡說八道,前面已經講了,get方法首先查詢session快取,沒有的話查詢二級快取,最後查詢資料庫;反而load方法建立時首先查詢session快取,沒有就建立代理,實際使用資料時才查詢二級快取和資料庫。
總之對於get和load的根本區別,一句話,hibernate對於load方法認為該資料在資料庫中一定存在,可以放心的使用代理來延遲載入,如果在使用過程中發現了問題,只能拋異常;而對於get方法,hibernate一定要獲取到真實的資料,否則返回null。