国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Hibernate中的取策略延遲加載

Fetching strategies(取策略)
Fetching stategies是指hibernate在需要關(guān)聯(lián)數(shù)據(jù)的時候所采用的取關(guān)聯(lián)數(shù)據(jù)的策略。這個策略既可以在O/R映射文件里配,也可以通過特殊的
HQL:或Criteria語句實現(xiàn)。
Hibernate定義了以下取策略:

Join fetching : Hibernate取關(guān)聯(lián)數(shù)據(jù)或集合是通過OUTER JOIN的方式,通過同一條select 語句來實現(xiàn)。

Select fetching:在沒有指定了lazy = "false"(既延遲加載有效)的情況下,通過另一條select 語句來獲得與已經(jīng)獲得的實體相關(guān)的實體或集合。當然
這種情況發(fā)生在用戶真正要獲得關(guān)聯(lián)對象的時候。

Subselect fetching:在沒有指定了lazy = "false"(既延遲加載有效)的情況下,先通過一條查詢語句獲得了一個實體集,  然后對這個實體集中的每一個對象通過另一條select 語句來獲得與它相關(guān)的實體或集合。
當然這種情況發(fā)生在用戶真正要獲得關(guān)聯(lián)對象的時候。

Batch fetching :它是為查詢數(shù)據(jù)提供的一種優(yōu)化策略。通過指定主鍵或外鍵的列表的方式來實現(xiàn)一條select 語句獲得一批實體或集合。

從另一個角度來看,hibernate的fetching 分成以下幾種。

Immediate fetching: 如果實體已經(jīng)被加載了,他的關(guān)聯(lián)對象,關(guān)聯(lián)集合,屬性也要及時加栽。

lazy collection fetching: 只有應用程序真正使用這個集合的時候,才加栽這個集合。

"Extra-lazy" collection fetching : hibernate 不加載一個集合的所有對象到內(nèi)存里,需要哪個個體,加載哪個。

Proxy fetching :當前對象的單值相關(guān)對象只有在調(diào)用它的主鍵外的其他屬性的get方法時才加載它。

"NO-proxy"fetching :當前對象的單值相關(guān)對象在它的實體變量被訪問的時候就被加載。相對于Proxy fetching來說,Proxy fetching更延遲。
(因為"NO-proxy"fetching即使是訪問關(guān)聯(lián)對象的主健,關(guān)聯(lián)對象都要被加載)。"NO-proxy"fetching對于應用來說更條理清晰。因為在應用
中沒有一個可見的proxy.
   個人認為可以這樣理解上述情況,假如在數(shù)據(jù)庫中存在兩張表 A,B.表A中有一個指向表B主健的外鍵。如果想知道A表中的某條
    數(shù)據(jù)對應B表中的那條記錄的主鍵。完全不用訪問B表,A表中的此條數(shù)據(jù)的外鍵值就是B表中對應數(shù)據(jù)的主鍵。所有只有訪問B表中
    對應數(shù)據(jù)的主鍵外其他屬性時,才需要加載B表中的這條數(shù)據(jù)。

Lazy attribute fetching :當前對象的某個屬性或單值相關(guān)對象只有在與它對應的實體變量被訪問的時候才加載。

Working with lazy associations
默認的情況下,Hibernate3 在獲取關(guān)聯(lián)對象集合的時候使用的是lazy策略,獲得單值關(guān)聯(lián)對象的時候使用的是lazy proxy策略。這樣的策略
幾乎適用所有的應用。

如果你設(shè)置了hibernate.default_batch_fetch_size,Hibernate就會通過批量獲取來優(yōu)化lazy fetching.

lazy fetching 會引起一個問題。就是關(guān)閉了hibernate session以后加載延遲加載的對象。這樣會引起異常。如下:

s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts"); // Error!

由于在session被關(guān)閉之前,permissions 沒有被初始化,所以它的數(shù)據(jù)沒有被加載。hibernate不支持已經(jīng)被分離的對象
的延遲加載。修改的方法是把相關(guān)代碼移到tx.commit()之前。

或者我們可以在配置文件里通過在關(guān)聯(lián)對象那里指定 lazy="false"來使關(guān)聯(lián)集合或?qū)ο蟛槐谎舆t加載。但是如果你定義太多的
非延遲加載對象,hibernate 在一次事務中可以需要把整個數(shù)據(jù)庫加載到內(nèi)存中。

從另一個角度來說,在一次事務中,我們經(jīng)常使用joint fetching 這種方式(它天生就不是延遲加載)來代替select fetching 這種方式。
下邊我們就要看到怎么自定義 取策略。在hibernate3中,單值和集合關(guān)聯(lián)對象的取策略的指定方式是一致的。

Tuning fetch strategies
默認的select fetching 這種取策略很容器導致N+1次select 操作這樣的問題。所以我們可以在配置文件里指定join fetching 策略。如下:
Cat對應的配置文件:
<set name="permissions"
fetch="join">
<key column="userId"/>
<one-to-many class="Permission"/>
</set>
Permission對應的配置文件:
<many-to-one name="mother" class="Cat" fetch="join"/>
在映射文件里定義的取策略會影響如下操作:
 由 get() 或load()執(zhí)行的取操作。
 操作關(guān)聯(lián)對象而引發(fā)的取操作。
 Criteria查詢。
 如果使用了subselect 這種取策略還會影響HQL這種查詢方式。

一般來說,我們不是通過在映射配置文件自定義取策略,而是通過在一個事務里,通過在特定的HQL里使用 left join 來覆蓋默認的取
策略。對于Criteria 來說,提供了setFetchMode(FetchMode.JOIN) API.如下:

User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();

另一種完全不同的避免N+1次selects 的方式是使用second-level cache.

Single-ended association proxies
集合的延遲加載是通過Hibernate自己的持久化集合實現(xiàn)的,但是對于單個相關(guān)對象的延遲加載
需要一個不同的機制.相關(guān)的對象必須被代理.Hibernate 對持久化對象的代理的延遲加載是通過
對運行時字節(jié)的動態(tài)注入實現(xiàn)的(通過CGLIB實現(xiàn)).

默認的情況下,Hibernate3為所有的持久化類生成代理,通過這些代理來完成 many-to-one 和
one-to-one 關(guān)聯(lián)對象的延遲加載.

在映射文件中可以為類聲明一個接口做為它的代理接口,通過proxy屬性指定。實際上,hibernate真正代理的是
這個類的子類。需要注意的是,被代理的類必須實現(xiàn)一個默認的構(gòu)造函數(shù)(此構(gòu)造函數(shù)的范圍至少是包內(nèi)可見的)。
推薦所有的持久化類使用這種構(gòu)造函數(shù)。我們現(xiàn)在可以看到的是在類的多態(tài)的時候會采用這種方式:

<class name="Cat" proxy="Cat">
......
<subclass name="DomesticCat">
.....
</subclass>
</class>

首先要注意的是,Cat的實例不能當作DomesticCat實例使用。即使Cat和DomesticCat對應的是同一條數(shù)據(jù)。
Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
DomesticCat dc = (DomesticCat) cat; // Error!
....
}

其次,兩者之間不能使用==
Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
DomesticCat dc =
(DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
System.out.println(cat==dc);

實際情況并非如我們看到的那么糟糕。即使我們引用了兩個不同的代理對象,實際的對象卻是相同的。
cat.setWeight(11.0); // hit the db to initialize the proxy
System.out.println( dc.getWeight() ); // 11.0

還需注意的是如果一個類是final class,或者它有final方法。我們就不能使用CGLIB代理.

最后,如果你的持久化對象在實例化的過程中獲得的任何資源(例如 在initializers或者默認的構(gòu)造函數(shù)里),這些
資源也將被proxy獲得.實際上代理的是這個類的子類。

這些問題的根源是java不能多重繼承.如果你想避免這些問題,你應該讓每一個類(子類和父類)實現(xiàn)一個聲明了業(yè)務方法的接口.
在你的映射文件中指定這些接口,如下:
<class name="CatImpl" proxy="Cat">
......
<subclass name="DomesticCatImpl" proxy="DomesticCat">
.....
</subclass>
</class>
CatImpl實現(xiàn)了接口Cat,DomesticCatImpl實現(xiàn)了接口DomesticCat.Cat和DomesticCat實例的代理可以
被load()或iterator()方法返回.(list()方法一般不返回代理).

Cat cat = (Cat) session.load(CatImpl.class, catid);
Iterator iter = session.iterate("from CatImpl as cat where cat.name=‘fritz‘");
Cat fritz = (Cat) iter.next();
關(guān)系也被延遲加載.這意味這你必須在Cat中聲明所有的屬性,而不僅僅是CatImpl.

以下方法不需要代理的初始值。
equals()  此方法沒有被覆蓋的時候。
hashCode() 此方法沒有被覆蓋的時候。
主鍵對應的get方法。

Initializing collections and proxies
如果在session的外邊訪問一個沒有初始化的集合或代理,會拋出一個LazyInitializationException異常。例如在分離的
狀態(tài)下(session 已經(jīng)close的情況下)訪問一個實體的延遲加載的集合或代理對象。

有時候我們需要在session關(guān)閉之前確保一個代理或集合被初始化。當然我們可以通過cat.getSex()或cat.getKittents().size()
這種方式來強迫初試化。但是這樣會使代碼閱讀者迷茫而且不是一種通用的方便的編碼格式。

靜態(tài)方法Hibernate.initialize() 和Hibernate.isInitialized()為應用提供了處理延遲加載集合或代理的一種便捷方式。
Hibernate.initialize(Cat) 會強制加載代理 cat. Hibernate.initialize(cat.getKittens())初始化kittens集合。當然這些方法要在
session關(guān)閉之前執(zhí)行。

另一種方式是在所有需要的集合和代理對象都被加載之后再關(guān)閉session. 在一些應用中,尤其是當應用使用hibernate來獲取
數(shù)據(jù),卻在其他的應用層處理這些數(shù)據(jù)?;蚴沁@些數(shù)據(jù)是在其他的處理過程中使用。為了確保這些集合在初始化的時候session
還處于打開狀態(tài),可以通過以下兩種方式:

1 基于 web 的應用可以通過filter 在一次請求的最后關(guān)閉session.當然這樣做是基于你的應用可以正確處理異常。非常重要的一點是要確保把信息返回給用戶之前把事務結(jié)束和把session關(guān)掉,即使是在你的頁面處理發(fā)生異常的情況下。(spring 的OpenSessionInViewFilter就是基于此開發(fā)出來的)

2 如果你的應用有一個單獨的業(yè)務層。在業(yè)務邏輯這里要保證在返回給web 層信息之前完成所有的集合初始化工作。這意味著你的
業(yè)務層需要加載所有的數(shù)據(jù)并且把這些包括延遲加載的數(shù)據(jù)傳給與一個特定的用戶請求的相關(guān)呈現(xiàn)部分。一般來說這是通過在session
關(guān)閉之前針對相關(guān)的集合調(diào)用Hibernate.initialize()方法或者是采用Criteria 的FetchMode.JOIN 方式。采用命令模式往往比采用session
Facade容易一些。

3 你也可以在訪問沒有初試化的集合(或代理)之前把先前加載的一個對象通過merge()或lock()放到新的Session里。但是hibernate 不
會也不應該自動完成這樣的工作,因為這樣需要使用特殊的事務處理語法。

有時候,你需要獲得集合中數(shù)據(jù)的個數(shù),或者集合數(shù)據(jù)的一部分就不需要初始話整個集合。你可以通過Collection filter來獲得集合中數(shù)據(jù)
的個數(shù)(不需要初始化整個集合)
( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()。
當然Collection filter也可以獲取集合的一部分數(shù)據(jù)
s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();

Using batch fetching
批量獲取數(shù)據(jù)可以提高Hibernate的效率.批量獲取是延遲select fetching策略的一種優(yōu)化.我們可以對類或者集合兩個角度采用批量取數(shù)據(jù).

批量獲取類/實體容易理解,假設(shè)有如下情況:
      在你的session里加載了25個Cat實例。每一個Cat都有一個own的引用指向一個person.在這里這個關(guān)聯(lián)的person是通過代理的方式延遲加載
 (單值關(guān)聯(lián)對象)。如果你現(xiàn)在要通過循環(huán)調(diào)用所有cat的getOwner()方法。hibernate會默認的執(zhí)行25個select 語句來獲得被代理的owner對象。
我們可以通過在Person這個表的映射文件中指定batch-size來實現(xiàn)批量取數(shù)據(jù)。
<class name="Person" batch-size="10">...</class>
Hibernate 現(xiàn)在會執(zhí)行三條查詢語句來完成查詢,模式是10,10,5.

你也可以對集合進行批量取操作.例如,每一個person都有一個被延遲加載的集合Cats.現(xiàn)在在session中已經(jīng)加載了10個 person實例.循環(huán)調(diào)用
所有的person的getCats()方法會產(chǎn)生10條select 語句.如果你在person的映射文件中定義了批量獲取模式:
<class name="Person">
<set name="cats" batch-size="3">
...
</set>
</class>
通過設(shè)置batch-size設(shè)為3,Hibernate 會以3,3,3,1的模式通過四條select語句加載集合。

Using subselect fetching
如果要加載一個延遲加載的集合或一個單值的代理,Hibernate通過一個subselect 運行原來的查詢語句,這種情況和batch-fetching是異曲同工的。

Using lazy property fetching
Hibernate支持對單個屬性的延遲加載。這個優(yōu)化技術(shù)也被 稱為fetch groups. 需要注意的是,這個技術(shù)還處于推銷階段。因為在實際中,對行的讀取
優(yōu)化比對列的優(yōu)化更重要。然而在一些特殊情況下,加載一個類的部分屬性還是有必要的,比如一個繼承的表有幾百列而且數(shù)據(jù)模型還不能改變。

為了使某個屬性被延遲加載,只需要在這個屬性的影射文件中加上lazy屬性即可。

<class name="Document">
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="summary" not-null="true" length="200" lazy="true"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
</class>

屬性的延遲加載需要使用運行時的字節(jié)設(shè)備來處理。如果你的持久化類還沒有被這個設(shè)備處理。hibernate 會忽略這個設(shè)置
采用及時加載的方式。

要想使用此字節(jié)設(shè)備處理持久化類,使用如下的Ant 任務。

<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
<classpath path="${jar.path}"/>
<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${testclasses.dir}/org/hibernate/auction/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>

另一種避免加載不需要的列的方式,至少在只讀事務中,是通過使用HQL或Criteria查詢屬性。這樣可以避免使用字節(jié)
處理工具。

你可以通過在HQL指定fetch all properties 來加載全部屬性。

本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
ssh框架問題
Hibernate4實戰(zhàn) 之第七部分:最佳實踐
Join用法,HQL的方法,Hibernate中的fetch
Spring,hibernate,struts常見的面試筆試題匯總
使用Spring hibernate no session or session was closed
Hibernate中,HQL參數(shù)綁定
更多類似文章 >>
生活服務
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服