hibernate的檢索策略
轉載請注明: TheViperhttp://m.survivalescaperooms.com/TheViper
先看個大概


1.類級別
立即檢索 <class lazy='false'>;延遲檢索<class lazy='true'>.默認為延遲檢索。
如果加載一個持久化對象是為了獲取它的屬性,用立即檢索;而如果僅僅是為了獲取它的引用,用延遲加載。比如,
Friend_Category fc = new Friend_Category(); fc.setCategory_name("已經存在的好友分類"); User_Friend_pk pk = new User_Friend_pk(); pk.setFriend_name(“新好友”); pk.setName("TheViper"); User_Friend uf = new User_Friend(); uf.setUser_friend_pk(pk); Friend_Category fc1 = (Friend_Category) session.load( Friend_Category.class, 1); uf.setFriend_categorys(fc1); // uf.setSort("好基友"); session.saveOrUpdate(fc1); session.save(uf);可以看到,加載Friend_Catagory類只是為了把新加好友加入到已經存在的好友分類里。這里用load()返回一個代理類,里面只有id屬性,其他屬性均為null,并沒有返回所有的屬性,因此占用的內存很少。
另外,不管<class> 的lazy屬性是true還是false,get(),list()總是用立即加載,但是并不會去加載與之相關聯的類(關聯級別檢索為默認設置延遲加載)。
2.關聯級別

由<set>的lazy和fetch屬性決定
lazy:決定關聯集合被初始化的時機。即是在加載時就被初始化,還是在被訪問時才初始化。
fetch:取值為select或subselect時,決定初始化關聯集合時查詢語句的形式;取值為fetch時,決定關聯集合被初始化的時機,這時顯式設置lazy屬性是無意義的。
lazy=true時,關聯集合類的代理在下面情況會被初始化:
1.應用程序第一次訪問它,如調用它的iterator(),size(),isEmpty()或contains().
2.通過org.hibernate.Hibernate類的initialize()靜態方法初始化。
對于lazy=extra,hibernate會進一步延遲關聯對象的初始化時機。具體說就是僅當應用程序第一次調用關聯對象的iterator()時才會加載。只有這一個方法能起作用。
對于lazy=false,
Feeling f = (Feeling) session.get(Feeling.class, 1);
<set name="feelingComments" inverse="true" lazy="false"> <key column="feeling_id" /> <one-to-many class="model.FeelingComment" /> </set>
當取出Feeling類時,便會一并取出FeelingComment類
Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?
下面設置fetch=subselect,
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Query q = session.createQuery("from Feeling feeling"); List<Feeling> feelings = (List<Feeling>) q.list(); for (Feeling feeling : feelings) { Set comments = feeling.getFeelingComments(); System.out.PRintln(comments); } transaction.commit(); session.close();Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id in ( select feeling0_.feeling_id from feeling feeling0_ )[model.FeelingComment@17f2fe3, model.FeelingComment@1211a4b, model.FeelingComment@533fc3][model.FeelingComment@a9a994, model.FeelingComment@455d96][model.FeelingComment@35c6f]
可以看到hibernate是用in子查詢的方式抓取關聯對象的,這時batch-size屬性會被忽略。
fetch=join表示采用左外連接的檢索策略抓取關聯對象,注意list()會自動忽略這個設置,這個很重要。
還是上面的代碼,結果
Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@1fbf047, model.FeelingComment@82a092, model.FeelingComment@15bd7c5]Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@c407e, model.FeelingComment@5e3212]Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@69757f]
而這時設置lazy,batch-size屬性沒有意義。
關于batch-size屬性,它用來為檢索策略設定批量檢索的數量,可以減少select語句的數量,提供檢索的運行性能。
比如batch-size設為2,關聯集合里面有3個對象。
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Query q = session.createQuery("from Feeling feeling"); List<Feeling> feelings = (List<Feeling>) q.list(); for (Feeling feeling : feelings) { Set comments = feeling.getFeelingComments(); System.out.println(comments); } transaction.commit(); session.close();Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id in ( ?, ? )[model.FeelingComment@177151, model.FeelingComment@16e125, model.FeelingComment@110a0ca]Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@1c8f247, model.FeelingComment@1d67998][model.FeelingComment@ac44e3]
注意,如果對Query接口用iterator()返回數據的話,會忽略batch-size!
Hibernate: select feeling0_.feeling_id as col_0_0_ from feeling feeling0_Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@ee7d2a, model.FeelingComment@fb3758, model.FeelingComment@17596bc]Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@855e17]Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?[model.FeelingComment@85ba73, model.FeelingComment@191bae7]
iterator()參見上一篇說明.

fetch=join
<set name="feelingComments" inverse="true"> <key column="feeling_id" /> <one-to-many class="model.FeelingComment" /> </set>
<many-to-one name="feelings" column="feeling_id" class="model.Feeling" not-null="false" fetch="join"> </many-to-one>
FeelingComment f = (FeelingComment) session .get(FeelingComment.class, 1);
Hibernate: select feelingcom0_.feelingComment_id as feelingC1_2_1_, feelingcom0_.content as content2_1_, feelingcom0_.feeling_id as feeling3_2_1_, feelingcom0_.id as id2_1_, feeling1_.feeling_id as feeling1_1_0_, feeling1_.content as content1_0_, feeling1_.id as id1_0_ from feeling_comment feelingcom0_ left outer join feeling feeling1_ on feelingcom0_.feeling_id=feeling1_.feeling_id where feelingcom0_.feelingComment_id =?
上面的很簡單。稍微變一下,在<set>添加lazy=false.
Hibernate: select feelingcom0_.feelingComment_id as feelingC1_2_1_, feelingcom0_.content as content2_1_, feelingcom0_.feeling_id as feeling3_2_1_, feelingcom0_.id as id2_1_, feeling1_.feeling_id as feeling1_1_0_, feeling1_.content as content1_0_, feeling1_.id as id1_0_ from feeling_comment feelingcom0_ left outer join feeling feeling1_ on feelingcom0_.feeling_id=feeling1_.feeling_id where feelingcom0_.feelingComment_id =?Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=?
可以看到,這次發出了兩條sql,第二次會再次根據feeling_id取出feeling_comment,個人感覺沒意義啊。而在一對多和多對多中就不會出現這種情況。
另外,list()也會像一對多和多對多,忽略fetch=join.
lazy=proxy(默認)
這個像一對多和多對多里面的lazy=true.即查詢“多”那邊時,不會查詢“一”那邊的關聯對象。只有當實際要用關聯對象時才會初始化它。
注意對于<one-to-one>,必須要設置constrained=true.表明“一”那邊的關聯對象不能為空。
lazy=no-proxy
這個和lazy=proxy很相似。不過需要編譯時字節碼增強,否則和proxy沒區別 。

這樣做了后的效果。
FeelingComment f = (FeelingComment) session .get(FeelingComment.class, 1); Feeling f1 = f.getFeelings(); f1.getContent();
當運行到getFeelings()時,hibernate會發出sql.而lazy=proxy時,要運行到getContent()時才發出。
lazy=false
當運行到get()時就會發出兩條sql.
Hibernate: select feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feelingComment_id =?Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?
另外如果
<many-to-one name="feelings" column="feeling_id" class="model.Feeling" not-null="false" lazy="false">
同時在“一”那邊
<class name="model.Feeling" table="feeling" lazy="false"> </class>
便會出現和fetch=join一樣的sql.
FeelingComment f = (FeelingComment) session .get(FeelingComment.class, 1);
Hibernate: select feelingcom0_.feelingComment_id as feelingC1_2_1_, feelingcom0_.content as content2_1_, feelingcom0_.feeling_id as feeling3_2_1_, feelingcom0_.id as id2_1_, feeling1_.feeling_id as feeling1_1_0_, feeling1_.content as content1_0_, feeling1_.id as id1_0_ from feeling_comment feelingcom0_ left outer join feeling feeling1_ on feelingcom0_.feeling_id=feeling1_.feeling_id where feelingcom0_.feelingComment_id =?
在多對一和一對一中使用批量檢索
在<many-to-one>,<one-to-one>中沒有batch-size屬性,需要“一”那邊設置batch-size.比如,需要加裝三個關聯實例(一),batch-size=2.
<class name="model.Feeling" table="feeling" batch-size="2"> </class>
Query q = session.createQuery("from FeelingComment"); List<FeelingComment> feelings = (List<FeelingComment>) q.list(); for (FeelingComment feelingComment : feelings) { Feeling feeling = feelingComment.getFeelings(); System.out.println(feeling); }Hibernate: select feelingcom0_.feelingComment_id as feelingC1_2_, feelingcom0_.content as content2_, feelingcom0_.feeling_id as feeling3_2_, feelingcom0_.id as id2_ from feeling_comment feelingcom0_Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id in ( ?, ? )model.Feeling@1e684bdmodel.Feeling@1e684bdmodel.Feeling@1e684bdmodel.Feeling@1149b6cHibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =?model.Feeling@fb8996model.Feeling@1149b6c
注意上面的例子<many-to-one>中的lazy是默認的proxy.如果lazy=false的話,當運行到feelingComment.getFeelings();時就會發出后面的sql了。
只需在<property>,<component>上設置lazy=true.這種策略適用于二進制大對象,字符串大對象,大容量組件類型的屬性。比如User類的image屬性儲存著圖片的二進制數據,
User user=(User)session.get(User.class,1);user.getImage();
get()時,hibernate沒有加載image屬性,運行到getImage()時才會加載。
注意,用的時候必須向多對一,一對一lazy=no-proxy一樣,需要編譯時字節碼增強。
新聞熱點
疑難解答