<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Java,J2EE,Weblogic,Oracle

    java項目隨筆
    隨筆 - 90, 文章 - 6, 評論 - 61, 引用 - 0
    數(shù)據(jù)加載中……

    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)的實體或集合。當(dāng)然
    這種情況發(fā)生在用戶真正要獲得關(guān)聯(lián)對象的時候。

    Subselect fetching:在沒有指定了lazy = "false"(既延遲加載有效)的情況下,先通過一條查詢語句獲得了一個實體集,  然后對這個實體集中的每一個對象通過另一條select 語句來獲得與它相關(guān)的實體或集合。
    當(dāng)然這種情況發(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: 只有應(yīng)用程序真正使用這個集合的時候,才加栽這個集合。

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

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

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

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

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

    如果你設(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 在一次事務(wù)中可以需要把整個數(shù)據(jù)庫加載到內(nèi)存中。

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

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

    一般來說,我們不是通過在映射配置文件自定義取策略,而是通過在一個事務(wù)里,通過在特定的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的實例不能當(dāng)作DomesticCat實例使用。即使Cat和DomesticCat對應(yīng)的是同一條數(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不能多重繼承.如果你想避免這些問題,你應(yīng)該讓每一個類(子類和父類)實現(xiàn)一個聲明了業(yè)務(wù)方法的接口.
    在你的映射文件中指定這些接口,如下:
    <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() 此方法沒有被覆蓋的時候。
    主鍵對應(yīng)的get方法。

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

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

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

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

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

    2 如果你的應(yīng)用有一個單獨的業(yè)務(wù)層。在業(yè)務(wù)邏輯這里要保證在返回給web 層信息之前完成所有的集合初始化工作。這意味著你的
    業(yè)務(wù)層需要加載所有的數(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 不
    會也不應(yīng)該自動完成這樣的工作,因為這樣需要使用特殊的事務(wù)處理語法。

    有時候,你需要獲得集合中數(shù)據(jù)的個數(shù),或者集合數(shù)據(jù)的一部分就不需要初始話整個集合。你可以通過Collection filter來獲得集合中數(shù)據(jù)
    的個數(shù)(不需要初始化整個集合)
    ( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()。
    當(dāng)然Collection filter也可以獲取集合的一部分?jǐn)?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 任務(wù)。

    <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>

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

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

    posted on 2008-07-21 17:22 龔椿深 閱讀(1125) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲网站在线免费观看| 无码欧精品亚洲日韩一区| 色老板亚洲视频免在线观| 亚洲高清免费在线观看| 亚洲视频国产精品| 最近中文字幕mv免费高清视频8| 亚洲韩国—中文字幕| 黄色片免费在线观看| 亚洲天堂男人天堂| 在线永久免费的视频草莓| 亚洲香蕉在线观看| 日韩a级毛片免费观看| 美女黄色免费网站| 久久夜色精品国产亚洲av| 国产免费阿v精品视频网址| 亚洲成AV人片在| 在线免费中文字幕| 亚洲人精品亚洲人成在线| 国产成人3p视频免费观看| 久青草国产免费观看| 亚洲av无码片在线播放| 日本XXX黄区免费看| 亚洲av无码专区在线观看下载| 亚洲av区一区二区三| 免费看无码特级毛片| 亚洲国产av高清无码| 国产精品久久香蕉免费播放| 一级有奶水毛片免费看| 久久久久亚洲AV无码专区首JN| 性色av免费观看| 亚洲免费无码在线| 亚洲午夜久久久精品电影院| 国产精品无码一二区免费| 两个人www免费高清视频| 亚洲欧洲国产视频| 免费国产成人午夜私人影视 | 日本在线高清免费爱做网站| 国产亚洲综合视频| 亚洲视频在线一区| 国产不卡免费视频| 69视频免费在线观看|