日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Hibernate n+1问题

發(fā)布時間:2025/7/14 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Hibernate n+1问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  Hibernate?中常會用到?set?,?bag?等集合表示?1?對多的關系,在獲取實體的時候就能根據(jù)關系將關聯(lián)的對象或者對象集取出,還可以設定?cacade?進行關聯(lián)更新和刪除。這不得不說?hibernate?的?orm?做得很好,很貼近?oo?的使用習慣了。

  但是對數(shù)據(jù)庫訪問還是必須考慮性能問題的,在設定了?1?對多這種關系之后,?查詢就會出現(xiàn)傳說中的?n+1?問題。

?

  出現(xiàn)的情況

一對多: 在一方,查找得到了 n 個對象,那么又需要將 n 個對象關聯(lián)的集合取出,于是本來的一條 sql 查詢變成了 n+1 條;多對一: 在多方,查詢得到了 m 個對象,那么也會將 m 個對象對應的 1 方的對象取出, 也變成了 m+1

?

?

  其實這個問題在Hibernate in Action中已經(jīng)有很多種解決辦法了。

  下面總結(jié)一下。

?

需求這樣的,我有四張表(one,two,three,four)從one一直外鍵關聯(lián)到four。結(jié)構(gòu)如下

現(xiàn)在在Session中得到One,并從One里一直取到Four里的內(nèi)容。如果簡單的用Session.get來實現(xiàn)是這樣的。

One one = (One)session.get(One.class,new Integer(1));Iterator iterone = one.getTwos().iterator();while(iterone.hasNext()){Two two = (Two) iterone.next();Iterator itertwo = two.getThrees().iterator();while(itertwo.hasNext()){Three three = (Three) itertwo.next();three.getFours().size(); }}

?

這樣我在Session關閉后返回的One里是從One到Four的信息都有的。
然而這樣做所導致的結(jié)果是生成大量的SQL查詢,這是一個典型的n+1 Selects問題。如果系統(tǒng)結(jié)構(gòu)層次多,符合條件的記錄多,那么Hibernate為你生成的SQL查詢將是難以接受的。
對于這個例子生成的SQL是這樣的

Hibernate: select one0_.c_one_id as c1_0_, one0_.c_one_text as c2_3_0_ from Oneone0_ where one0_.c_one_id=? Hibernate: select twos0_.c_one_id as c2_1_, twos0_.c_two_id as c1_1_,twos0_.c_two_id as c1_0_, twos0_.c_one_id as c2_2_0_, twos0_.c_two_text asc3_2_0_ from Two twos0_ where twos0_.c_one_id=? Hibernate: select threes0_.c_two_id as c2_1_, threes0_.c_three_id as c1_1_,threes0_.c_three_id as c1_0_, threes0_.c_two_id as c2_1_0_,threes0_.c_three_text as c3_1_0_ from Three threes0_ where threes0_.c_two_id=? Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_,fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_textas c3_0_0_ from Four fours0_ where fours0_.c_three_id=? Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_,fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_textas c3_0_0_ from Four fours0_ where fours0_.c_three_id=? Hibernate: select threes0_.c_two_id as c2_1_, threes0_.c_three_id as c1_1_,threes0_.c_three_id as c1_0_, threes0_.c_two_id as c2_1_0_,threes0_.c_three_text as c3_1_0_ from Three threes0_ where threes0_.c_two_id=? Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_,fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_textas c3_0_0_ from Four fours0_ where fours0_.c_three_id=? Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_,fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_textas c3_0_0_ from Four fours0_ where fours0_.c_three_id=?

?


對于這樣的問題,在沒有Hibernate以前我們一般都用jdbc來做,那樣的話我們其實用一個進行3次join的sql語句就可以實現(xiàn),但是這樣解決也有問題,就是返回的ResultSet中的數(shù)據(jù)非常多,而且雜亂,其實是從one到four平行排列的。對于這樣的結(jié)果集我們要把它手動影射曾對象結(jié)構(gòu)也是一個很復雜的操作。
幸好Hibernate3可以為我們做這些事情(我再一次被Hibernate的強大所震撼)。
上面的實現(xiàn)可以用Criteria來實現(xiàn):

session = sessionFactory.openSession();Criteria criteria = session.createCriteria(One.class);criteria.add(Expression.eq("COneId",new Integer(1)));one = (One)criteria.setFetchMode("twos",FetchMode.JOIN).setFetchMode("twos.threes",FetchMode.JOIN).setFetchMode("twos.threes.fours",FetchMode.JOIN).uniqueResult();session.close();

?

這里的重點是這句話

criteria.setFetchMode("twos",FetchMode.JOIN)
.setFetchMode("twos.threes",FetchMode.JOIN)
.setFetchMode("twos.threes.fours",FetchMode.JOIN)
.uniqueResult();

?

  在用Criteria之前先設置FetchMode,應為Criteria是動態(tài)生成sql語句的,所以生成的sql就是一層層Join下去的。
setFetchMode(String,Mode)第一個參數(shù)是association path,用"."來表示路徑。這一點具體的例子很少,文檔也沒有寫清楚。我也是試了很久才試出來的。
就這個例子來所把因為取道第四層,所以要進行三次setFetchMode
第一次的路徑是twos,一位one中有two的Set。這個具體要更具hbm.xml的配置來定。
第二個路徑就是twos.threes
第三個就是twos.threes.fours
一次類推,一層層增加的。
這樣做法最終生成的SQL是這樣的:

Hibernate: select this_.c_one_id as c1_3_, this_.c_one_text as c2_3_3_,twos2_.c_one_id as c2_5_, twos2_.c_two_id as c1_5_, twos2_.c_two_id as c1_0_,twos2_.c_one_id as c2_2_0_, twos2_.c_two_text as c3_2_0_, threes3_.c_two_id asc2_6_, threes3_.c_three_id as c1_6_, threes3_.c_three_id as c1_1_,threes3_.c_two_id as c2_1_1_, threes3_.c_three_text as c3_1_1_,fours4_.c_three_id as c2_7_, fours4_.c_four_id as c1_7_, fours4_.c_four_id asc1_2_, fours4_.c_three_id as c2_0_2_, fours4_.c_four_text as c3_0_2_ from Onethis_ left outer join Two twos2_ on this_.c_one_id=twos2_.c_one_id left outerjoin Three threes3_ on twos2_.c_two_id=threes3_.c_two_id left outer join Fourfours4_ on threes3_.c_three_id=fours4_.c_three_id where this_.c_one_id=?

?


雖然很長但是只有一條SQL語句。性能要好很多。Hibernate的強大之處是它會把返回的ResultSet自動影射到你的對象模型里面去。這就為我們省了很多事。

看來Hibernate真是一個耐人尋味的Framework啊。

源碼,沒什么東西。

?

?

Hibernate N+1 問題及解決辦法

? 1、?使用?fetch?抓取,?Hibernate?抓取策略分為單端代理和集合代理的抓取策略。

Hibernate?抓取策略?(?單端代理的抓取策略?)?

???保持默認也就是如下?:

<many-to-one name="clazz" cascade="save-update" fetch="select" />

?

????fetch="select"?就是另外發(fā)送一條?select?語句抓取當前對象關聯(lián)實體或者集合設置?fetch="join"??

<many-to-one name="clazz" cascade="save-update" fetch="join"/>

?

Hibernate?會通過?select?語句使用外連接來加載器關聯(lián)實體活集合此時?lazy?會失效

???????Hibernate?抓取策略?(?集合代理的抓取策略?)?

????????????保持默認(?fetch="select"?)也就是如下?:? ? ? ??

<set name="students" inverse="true"><key column="clazz"/><one-to-many class="com.june.hibernate.Student"/> </set>

?

?????????????1)fetch="select"?會另外發(fā)出一條語句查詢集合

?????????????2)?設置?fetch="join"?采用外連接集合的?lazy?失效

?????????????3)?這只?fetch="subselect"?另外發(fā)出一條?select?語句抓取前面查詢到的所有的實體對象的關聯(lián)集合?fetch只對?HQL?查詢產(chǎn)生影響其他的則不會

?

? ? ???2、?使用?map?直接搜索需要的列

  如:產(chǎn)品?product?和產(chǎn)品分類?product_category?兩張表,多對一關系。查詢產(chǎn)品列表時

select new Map(p.id as id, p.name as name, p.category.name as categoryName) from Product p

?

?

? ? ? ?3、?延遲檢索策略

  延遲檢索策略能避免多余加載應用程序不需要訪問的關聯(lián)對象,

  hibernate3開始已經(jīng)默認是lazy=true了;lazy=true時不會立刻查詢關聯(lián)對象,只有當需要關聯(lián)對象(訪問其屬性)時才會發(fā)生查詢動作。

?? ? ? ?4、?迫切左外連接檢索策略

  迫切左外連接檢索策略則充分利用了SQL的外連接查詢功能,能夠減少select語句的數(shù)目。

  可以在映射文件中定義連接抓取方式。

<set name=”orders” fetch=”join”><key column=”customer_id”><one-to-many class="com.hibernate.mappings.Order"/></set>

?

或者使用HQL的LEFT OUTER JOIN.

或者在條件查詢中使用setFetchMode(FetchMode.JOIN)

Customer ctm = (Customer)session.createCriteria(Customer.class).setFetchMode(“Order”.JOIN).add(Restrictions.idEq(customer_id));

?

轉(zhuǎn)載于:https://www.cnblogs.com/hwaggLee/p/4440263.html

總結(jié)

以上是生活随笔為你收集整理的Hibernate n+1问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。