欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
第20章提升性能

第 20 章 提升性能

20.1.  抓取策略(Fetching strategies)

抓取策略(fetching strategy) 是指:當應用程序需要在(Hibernate實(shí)體對象圖的)關(guān)聯(lián)關(guān)系間進(jìn)行導航的時(shí)候, Hibernate如何獲取關(guān)聯(lián)對象的策略。抓取策略可以在O/R映射的元數據中聲明,也可以在特定的HQL 或條件查詢(xún)(Criteria Query)中重載聲明。

Hibernate3 定義了如下幾種抓取策略:

  • 連接抓?。↗oin fetching) - Hibernate通過(guò) 在SELECT語(yǔ)句使用OUTER JOIN(外連接)來(lái) 獲得對象的關(guān)聯(lián)實(shí)例或者關(guān)聯(lián)集合。

  • 查詢(xún)抓?。⊿elect fetching) - 另外發(fā)送一條 SELECT 語(yǔ)句抓取當前對象的關(guān)聯(lián)實(shí)體或集合。除非你顯式的指定lazy="false"禁止 延遲抓?。╨azy fetching),否則只有當你真正訪(fǎng)問(wèn)關(guān)聯(lián)關(guān)系的時(shí)候,才會(huì )執行第二條select語(yǔ)句。

  • 子查詢(xún)抓?。⊿ubselect fetching) - 另外發(fā)送一條SELECT 語(yǔ)句抓取在前面查詢(xún)到(或者抓取到)的所有實(shí)體對象的關(guān)聯(lián)集合。除非你顯式的指定lazy="false" 禁止延遲抓?。╨azy fetching),否則只有當你真正訪(fǎng)問(wèn)關(guān)聯(lián)關(guān)系的時(shí)候,才會(huì )執行第二條select語(yǔ)句。

  • 批量抓?。˙atch fetching) - 對查詢(xún)抓取的優(yōu)化方案, 通過(guò)指定一個(gè)主鍵或外鍵列表,Hibernate使用單條SELECT語(yǔ)句獲取一批對象實(shí)例或集合。

Hibernate會(huì )區分下列各種情況:

  • Immediate fetching,立即抓取 - 當宿主被加載時(shí),關(guān)聯(lián)、集合或屬性被立即抓取。

  • Lazy collection fetching,延遲集合抓取- 直到應用程序對集合進(jìn)行了一次操作時(shí),集合才被抓取。(對集合而言這是默認行為。)

  • Proxy fetching,代理抓取 - 對返回單值的關(guān)聯(lián)而言,當其某個(gè)方法被調用,而非對其關(guān)鍵字進(jìn)行g(shù)et操作時(shí)才抓取。

  • Lazy attribute fetching,屬性延遲加載 - 對屬性或返回單值的關(guān)聯(lián)而言,當其實(shí)例變量被訪(fǎng)問(wèn)的時(shí)候進(jìn)行抓?。ㄐ枰\行時(shí)字節碼強化)。這一方法很少是必要的。

這里有兩個(gè)正交的概念:關(guān)聯(lián)何時(shí)被抓取,以及被如何抓?。〞?huì )采用什么樣的SQL語(yǔ)句)。不要混淆它們!我們使用抓取來(lái)改善性能。我們使用延遲來(lái)定義一些契約,對某特定類(lèi)的某個(gè)脫管的實(shí)例,知道有哪些數據是可以使用的。

20.1.1. 操作延遲加載的關(guān)聯(lián)

默認情況下,Hibernate 3對集合使用延遲select抓取,對返回單值的關(guān)聯(lián)使用延遲代理抓取。對幾乎是所有的應用而言,其絕大多數的關(guān)聯(lián),這種策略都是有效的。

注意:假若你設置了hibernate.default_batch_fetch_size,Hibernate會(huì )對延遲加載采取批量抓取優(yōu)化措施(這種優(yōu)化也可能會(huì )在更細化的級別打開(kāi))。

然而,你必須了解延遲抓取帶來(lái)的一個(gè)問(wèn)題。在一個(gè)打開(kāi)的Hibernate session上下文之外調用延遲集合會(huì )導致一次意外。比如:

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)閉后,permessions集合將是未實(shí)例化的、不再可用,因此無(wú)法正常載入其狀態(tài)。 Hibernate對脫管對象不支持延遲實(shí)例化. 這里的修改方法是:將permissions讀取數據的代碼 移到tx.commit()之前。

除此之外,通過(guò)對關(guān)聯(lián)映射指定lazy="false",我們也可以使用非延遲的集合或關(guān)聯(lián)。但是, 對絕大部分集合來(lái)說(shuō),更推薦使用延遲方式抓取數據。如果在你的對象模型中定義了太多的非延遲關(guān)聯(lián),Hibernate最終幾乎需要在每個(gè)事務(wù)中載入整個(gè)數據庫到內存中!

但是,另一方面,在一些特殊的事務(wù)中,我們也經(jīng)常需要使用到連接抓?。ㄋ旧砩暇褪欠茄舆t的),以代替查詢(xún)抓取。 下面我們將會(huì )很快明白如何具體的定制Hibernate中的抓取策略。在Hibernate3中,具體選擇哪種抓取策略的機制是和選擇 單值關(guān)聯(lián)或集合關(guān)聯(lián)相一致的。

20.1.2.  調整抓取策略(Tuning fetch strategies)

查詢(xún)抓?。J的)在N+1查詢(xún)的情況下是極其脆弱的,因此我們可能會(huì )要求在映射文檔中定義使用連接抓?。?

<set name="permissions"fetch="join"><key column="userId"/><one-to-many class="Permission"/></set
<many-to-one name="mother" class="Cat" fetch="join"/>

在映射文檔中定義的抓取策略將會(huì )有產(chǎn)生以下影響:

  • 通過(guò)get()load()方法取得數據。

  • 只有在關(guān)聯(lián)之間進(jìn)行導航時(shí),才會(huì )隱式的取得數據(延遲抓取)。

  • 條件查詢(xún)

通常情況下,我們并不使用映射文檔進(jìn)行抓取策略的定制。更多的是,保持其默認值,然后在特定的事務(wù)中, 使用HQL的左連接抓?。╨eft join fetch) 對其進(jìn)行重載。這將通知 Hibernate在第一次查詢(xún)中使用外部關(guān)聯(lián)(outer join),直接得到其關(guān)聯(lián)數據。 在條件查詢(xún) API中,應該調用 setFetchMode(FetchMode.JOIN)語(yǔ)句。

也許你喜歡僅僅通過(guò)條件查詢(xún),就可以改變get()load()語(yǔ)句中的數據抓取策略。例如:

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

(這就是其他ORM解決方案的“抓取計劃(fetch plan)”在Hibernate中的等價(jià)物。)

截然不同的一種避免N+1次查詢(xún)的方法是,使用二級緩存。

20.1.3. 單端關(guān)聯(lián)代理(Single-ended association proxies)

在Hinerbate中,對集合的延遲抓取的采用了自己的實(shí)現方法。但是,對于單端關(guān)聯(lián)的延遲抓取,則需要采用 其他不同的機制。單端關(guān)聯(lián)的目標實(shí)體必須使用代理,Hihernate在運行期二進(jìn)制級(通過(guò)優(yōu)異的CGLIB庫), 為持久對象實(shí)現了延遲載入代理。

默認的,Hibernate3將會(huì )為所有的持久對象產(chǎn)生代理(在啟動(dòng)階段),然后使用他們實(shí)現 多對一(many-to-one)關(guān)聯(lián)和一對一(one-to-one) 關(guān)聯(lián)的延遲抓取。

在映射文件中,可以通過(guò)設置proxy屬性為目標class聲明一個(gè)接口供代理接口使用。 默認的,Hibernate將會(huì )使用該類(lèi)的一個(gè)子類(lèi)。 注意:被代理的類(lèi)必須實(shí)現一個(gè)至少包可見(jiàn)的默認構造函數,我們建議所有的持久類(lèi)都應擁有這樣的構造函數

在如此方式定義一個(gè)多態(tài)類(lèi)的時(shí)候,有許多值得注意的常見(jiàn)性的問(wèn)題,例如:

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

首先,Cat實(shí)例永遠不可以被強制轉換為DomesticCat, 即使它本身就是DomesticCat實(shí)例。

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 proxyDomesticCat dc = (DomesticCat) cat;       // Error!....}

其次,代理的“==”可能不再成立。

Cat cat = (Cat) session.load(Cat.class, id);            // instantiate a Cat proxyDomesticCat dc =(DomesticCat) session.load(DomesticCat.class, id);  // acquire new DomesticCat proxy!System.out.println(cat==dc);                            // false

雖然如此,但實(shí)際情況并沒(méi)有看上去那么糟糕。雖然我們現在有兩個(gè)不同的引用,分別指向這兩個(gè)不同的代理對象, 但實(shí)際上,其底層應該是同一個(gè)實(shí)例對象:

cat.setWeight(11.0);  // hit the db to initialize the proxySystem.out.println( dc.getWeight() );  // 11.0

第三,你不能對“final類(lèi)”或“具有final方法的類(lèi)”使用CGLIB代理。

最后,如果你的持久化對象在實(shí)例化時(shí)需要某些資源(例如,在實(shí)例化方法、默認構造方法中), 那么代理對象也同樣需要使用這些資源。實(shí)際上,代理類(lèi)是持久化類(lèi)的子類(lèi)。

這些問(wèn)題都源于Java的單根繼承模型的天生限制。如果你希望避免這些問(wèn)題,那么你的每個(gè)持久化類(lèi)必須實(shí)現一個(gè)接口, 在此接口中已經(jīng)聲明了其業(yè)務(wù)方法。然后,你需要在映射文檔中再指定這些接口。例如:

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

這里CatImpl實(shí)現了Cat接口, DomesticCatImpl實(shí)現DomesticCat接口。 在load()、iterate()方法中就會(huì )返回 CatDomesticCat的代理對象。 (注意list()并不會(huì )返回代理對象。)

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)系也將被延遲載入。這就意味著(zhù),你應該將屬性聲明為Cat,而不是CatImpl。

但是,在有些方法中是不需要使用代理的。例如:

  • equals()方法,如果持久類(lèi)沒(méi)有重載equals()方法。

  • hashCode()方法,如果持久類(lèi)沒(méi)有重載hashCode()方法。

  • 標志符的getter方法。

Hibernate將會(huì )識別出那些重載了equals()、或hashCode()方法的持久化類(lèi)。

20.1.4. 實(shí)例化集合和代理(Initializing collections and proxies)

Session范圍之外訪(fǎng)問(wèn)未初始化的集合或代理,Hibernate將會(huì )拋出LazyInitializationException異常。 也就是說(shuō),在分離狀態(tài)下,訪(fǎng)問(wèn)一個(gè)實(shí)體所擁有的集合,或者訪(fǎng)問(wèn)其指向代理的屬性時(shí),會(huì )引發(fā)此異常。

有時(shí)候我們需要保證某個(gè)代理或者集合在Session關(guān)閉前就已經(jīng)被初始化了。 當然,我們可以通過(guò)強行調用cat.getSex()或者cat.getKittens().size()之類(lèi)的方法來(lái)確保這一點(diǎn)。 但是這樣的程序會(huì )造成讀者的疑惑,也不符合通常的代碼規范。

靜態(tài)方法Hibernate.initialized() 為你的應用程序提供了一個(gè)便捷的途徑來(lái)延遲加載集合或代理。 只要它的Session處于open狀態(tài),Hibernate.initialize(cat) 將會(huì )為cat強制對代理實(shí)例化。 同樣,Hibernate.initialize( cat.getKittens() ) 對kittens的集合具有同樣的功能。

還有另外一種選擇,就是保持Session一直處于open狀態(tài),直到所有需要的集合或代理都被載入。 在某些應用架構中,特別是對于那些使用Hibernate進(jìn)行數據訪(fǎng)問(wèn)的代碼,以及那些在不同應用層和不同物理進(jìn)程中使用Hibernate的代碼。 在集合實(shí)例化時(shí),如何保證Session處于open狀態(tài)經(jīng)常會(huì )是一個(gè)問(wèn)題。有兩種方法可以解決此問(wèn)題:

  • 在一個(gè)基于Web的應用中,可以利用servlet過(guò)濾器(filter),在用戶(hù)請求(request)結束、頁(yè)面生成 結束時(shí)關(guān)閉Session(這里使用了在展示層保持打開(kāi)Session模式(Open Session in View)), 當然,這將依賴(lài)于應用框架中異常需要被正確的處理。在返回界面給用戶(hù)之前,乃至在生成界面過(guò)程中發(fā)生異常的情況下, 正確關(guān)閉Session和結束事務(wù)將是非常重要的, Servlet過(guò)濾器必須如此訪(fǎng)問(wèn)Session,才能保證正確使用Session。 我們推薦使用ThreadLocal 變量保存當前的Session (可以參考第 1.4 節 “與Cat同樂(lè )”的例子實(shí)現)。

  • 在一個(gè)擁有單獨業(yè)務(wù)層的應用中,業(yè)務(wù)層必須在返回之前,為web層“準備”好其所需的數據集合。這就意味著(zhù) 業(yè)務(wù)層應該載入所有表現層/web層所需的數據,并將這些已實(shí)例化完畢的數據返回。通常,應用程序應該 為web層所需的每個(gè)集合調用Hibernate.initialize()(這個(gè)調用必須發(fā)生咱session關(guān)閉之前); 或者使用帶有FETCH從句,或FetchMode.JOIN的Hibernate查詢(xún), 事先取得所有的數據集合。如果你在應用中使用了Command模式,代替Session Facade , 那么這項任務(wù)將會(huì )變得簡(jiǎn)單的多。

  • 你也可以通過(guò)merge()lock()方法,在訪(fǎng)問(wèn)未實(shí)例化的集合(或代理)之前, 為先前載入的對象綁定一個(gè)新的Session。 顯然,Hibernate將不會(huì ),也不應該自動(dòng)完成這些任務(wù),因為這將引入一個(gè)特殊的事務(wù)語(yǔ)義。

有時(shí)候,你并不需要完全實(shí)例化整個(gè)大的集合,僅需要了解它的部分信息(例如其大?。?、或者集合的部分內容。

你可以使用集合過(guò)濾器得到其集合的大小,而不必實(shí)例化整個(gè)集合:

( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()

這里的createFilter()方法也可以被用來(lái)有效的抓取集合的部分內容,而無(wú)需實(shí)例化整個(gè)集合:

s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();

20.1.5. 使用批量抓?。║sing batch fetching)

Hibernate可以充分有效的使用批量抓取,也就是說(shuō),如果僅一個(gè)訪(fǎng)問(wèn)代理(或集合),那么Hibernate將不載入其他未實(shí)例化的代理。 批量抓取是延遲查詢(xún)抓取的優(yōu)化方案,你可以在兩種批量抓取方案之間進(jìn)行選擇:在類(lèi)級別和集合級別。

類(lèi)/實(shí)體級別的批量抓取很容易理解。假設你在運行時(shí)將需要面對下面的問(wèn)題:你在一個(gè)Session中載入了25個(gè) Cat實(shí)例,每個(gè)Cat實(shí)例都擁有一個(gè)引用成員owner, 其指向Person,而Person類(lèi)是代理,同時(shí)lazy="true"。 如果你必須遍歷整個(gè)cats集合,對每個(gè)元素調用getOwner()方法,Hibernate將會(huì )默認的執行25次SELECT查詢(xún), 得到其owner的代理對象。這時(shí),你可以通過(guò)在映射文件的Person屬性,顯式聲明batch-size,改變其行為:

<class name="Person" batch-size="10">...</class>

隨之,Hibernate將只需要執行三次查詢(xún),分別為10、10、 5。

你也可以在集合級別定義批量抓取。例如,如果每個(gè)Person都擁有一個(gè)延遲載入的Cats集合, 現在,Sesssion中載入了10個(gè)person對象,遍歷person集合將會(huì )引起10次SELECT查詢(xún), 每次查詢(xún)都會(huì )調用getCats()方法。如果你在Person的映射定義部分,允許對cats批量抓取, 那么,Hibernate將可以預先抓取整個(gè)集合。請看例子:

<class name="Person"><set name="cats" batch-size="3">...</set></class>

如果整個(gè)的batch-size是3(筆誤?),那么Hibernate將會(huì )分四次執行SELECT查詢(xún), 按照3、3、3、1的大小分別載入數據。這里的每次載入的數據量還具體依賴(lài)于當前Session中未實(shí)例化集合的個(gè)數。

如果你的模型中有嵌套的樹(shù)狀結構,例如典型的帳單-原料結構(bill-of-materials pattern),集合的批量抓取是非常有用的。 (盡管在更多情況下對樹(shù)進(jìn)行讀取時(shí),嵌套集合(nested set)原料路徑(materialized path)(××) 是更好的解決方法。)

20.1.6. 使用子查詢(xún)抓?。║sing subselect fetching)

假若一個(gè)延遲集合或單值代理需要抓取,Hibernate會(huì )使用一個(gè)subselect重新運行原來(lái)的查詢(xún),一次性讀入所有的實(shí)例。這和批量抓取的實(shí)現方法是一樣的,不會(huì )有破碎的加載。

20.1.7. 使用延遲屬性抓?。║sing lazy property fetching)

Hibernate3對單獨的屬性支持延遲抓取,這項優(yōu)化技術(shù)也被稱(chēng)為組抓?。╢etch groups)。 請注意,該技術(shù)更多的屬于市場(chǎng)特性。在實(shí)際應用中,優(yōu)化行讀取比優(yōu)化列讀取更重要。但是,僅載入類(lèi)的部分屬性在某些特定情況下會(huì )有用,例如在原有表中擁有幾百列數據、數據模型無(wú)法改動(dòng)的情況下。

可以在映射文件中對特定的屬性設置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>

屬性的延遲載入要求在其代碼構建時(shí)加入二進(jìn)制指示指令(bytecode instrumentation),如果你的持久類(lèi)代碼中未含有這些指令, Hibernate將會(huì )忽略這些屬性的延遲設置,仍然將其直接載入。

你可以在A(yíng)nt的Task中,進(jìn)行如下定義,對持久類(lèi)代碼加入“二進(jìn)制指令。”

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

還有一種可以?xún)?yōu)化的方法,它使用HQL或條件查詢(xún)的投影(projection)特性,可以避免讀取非必要的列, 這一點(diǎn)至少對只讀事務(wù)是非常有用的。它無(wú)需在代碼構建時(shí)“二進(jìn)制指令”處理,因此是一個(gè)更加值得選擇的解決方法。

有時(shí)你需要在HQL中通過(guò)抓取所有屬性,強行抓取所有內容。

20.2. 二級緩存(The Second Level Cache)

Hibernate的Session在事務(wù)級別進(jìn)行持久化數據的緩存操作。 當然,也有可能分別為每個(gè)類(lèi)(或集合),配置集群、或JVM級別(SessionFactory級別)的緩存。 你甚至可以為之插入一個(gè)集群的緩存。注意,緩存永遠不知道其他應用程序對持久化倉庫(數據庫)可能進(jìn)行的修改 (即使可以將緩存數據設定為定期失效)。

默認情況下,Hibernate使用EHCache進(jìn)行JVM級別的緩存(目前,Hibernate已經(jīng)廢棄了對JCS的支持,未來(lái)版本中將會(huì )去掉它)。 你可以通過(guò)設置hibernate.cache.provider_class屬性,指定其他的緩存策略, 該緩存策略必須實(shí)現org.hibernate.cache.CacheProvider接口。

表 20.1.  緩存策略提供商(Cache Providers)

Cache Provider class Type Cluster Safe Query Cache Supported
Hashtable (not intended for production use) org.hibernate.cache.HashtableCacheProvider memory   yes
EHCache org.hibernate.cache.EhCacheProvider memory, disk   yes
OSCache org.hibernate.cache.OSCacheProvider memory, disk   yes
SwarmCache org.hibernate.cache.SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)  
JBoss TreeCache org.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactional yes (replication) yes (clock sync req.)

20.2.1. 緩存映射(Cache mappings)

類(lèi)或者集合映射的“<cache>元素”可以有下列形式:

<cacheusage="transactional|read-write|nonstrict-read-write|read-only"  (1)/>
(1)

usage說(shuō)明了緩存的策略: transactional、 read-write、 nonstrict-read-writeread-only。

另外(首選?), 你可以在hibernate.cfg.xml中指定<class-cache><collection-cache> 元素。

這里的usage 屬性指明了緩存并發(fā)策略(cache concurrency strategy)。

20.2.2. 策略:只讀緩存(Strategy: read only)

如果你的應用程序只需讀取一個(gè)持久化類(lèi)的實(shí)例,而無(wú)需對其修改, 那么就可以對其進(jìn)行只讀 緩存。這是最簡(jiǎn)單,也是實(shí)用性最好的方法。甚至在集群中,它也能完美地運作。

<class name="eg.Immutable" mutable="false"><cache usage="read-only"/>....</class>

20.2.3.  策略:讀/寫(xiě)緩存(Strategy: read/write)

如果應用程序需要更新數據,那么使用讀/寫(xiě)緩存 比較合適。 如果應用程序要求“序列化事務(wù)”的隔離級別(serializable transaction isolation level),那么就決不能使用這種緩存策略。 如果在JTA環(huán)境中使用緩存,你必須指定hibernate.transaction.manager_lookup_class屬性的值, 通過(guò)它,Hibernate才能知道該應用程序中JTA的TransactionManager的具體策略。 在其它環(huán)境中,你必須保證在Session.close()、或Session.disconnect()調用前, 整個(gè)事務(wù)已經(jīng)結束。 如果你想在集群環(huán)境中使用此策略,你必須保證底層的緩存實(shí)現支持鎖定(locking)。Hibernate內置的緩存策略并不支持鎖定功能。

<class name="eg.Cat" .... ><cache usage="read-write"/>....<set name="kittens" ... ><cache usage="read-write"/>....</set></class>

20.2.4.  策略:非嚴格讀/寫(xiě)緩存(Strategy: nonstrict read/write)

如果應用程序只偶爾需要更新數據(也就是說(shuō),兩個(gè)事務(wù)同時(shí)更新同一記錄的情況很不常見(jiàn)),也不需要十分嚴格的事務(wù)隔離, 那么比較適合使用非嚴格讀/寫(xiě)緩存策略。如果在JTA環(huán)境中使用該策略, 你必須為其指定hibernate.transaction.manager_lookup_class屬性的值, 在其它環(huán)境中,你必須保證在Session.close()、或Session.disconnect()調用前, 整個(gè)事務(wù)已經(jīng)結束。

20.2.5.  策略:事務(wù)緩存(transactional)

Hibernate的事務(wù)緩存策略提供了全事務(wù)的緩存支持, 例如對JBoss TreeCache的支持。這樣的緩存只能用于JTA環(huán)境中,你必須指定 為其hibernate.transaction.manager_lookup_class屬性。

沒(méi)有一種緩存提供商能夠支持上列的所有緩存并發(fā)策略。下表中列出了各種提供器、及其各自適用的并發(fā)策略。

表 20.2.  各種緩存提供商對緩存并發(fā)策略的支持情況(Cache Concurrency Strategy Support)

Cache read-only nonstrict-read-write read-write transactional
Hashtable (not intended for production use) yes yes yes  
EHCache yes yes yes  
OSCache yes yes yes  
SwarmCache yes yes    
JBoss TreeCache yes     yes

20.3.  管理緩存(Managing the caches)

無(wú)論何時(shí),當你給save()、update()saveOrUpdate()方法傳遞一個(gè)對象時(shí),或使用load()、 get()、list()、iterate()scroll()方法獲得一個(gè)對象時(shí), 該對象都將被加入到Session的內部緩存中。

當隨后flush()方法被調用時(shí),對象的狀態(tài)會(huì )和數據庫取得同步。 如果你不希望此同步操作發(fā)生,或者你正處理大量對象、需要對有效管理內存時(shí),你可以調用evict() 方法,從一級緩存中去掉這些對象及其集合。

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result setwhile ( cats.next() ) {Cat cat = (Cat) cats.get(0);doSomethingWithACat(cat);sess.evict(cat);}

Session還提供了一個(gè)contains()方法,用來(lái)判斷某個(gè)實(shí)例是否處于當前session的緩存中。

如若要把所有的對象從session緩存中徹底清除,則需要調用Session.clear()。

對于二級緩存來(lái)說(shuō),在SessionFactory中定義了許多方法, 清除緩存中實(shí)例、整個(gè)類(lèi)、集合實(shí)例或者整個(gè)集合。

sessionFactory.evict(Cat.class, catId); //evict a particular CatsessionFactory.evict(Cat.class);  //evict all CatssessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittenssessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections

CacheMode參數用于控制具體的Session如何與二級緩存進(jìn)行交互。

  • CacheMode.NORMAL - 從二級緩存中讀、寫(xiě)數據。

  • CacheMode.GET - 從二級緩存中讀取數據,僅在數據更新時(shí)對二級緩存寫(xiě)數據。

  • CacheMode.PUT - 僅向二級緩存寫(xiě)數據,但不從二級緩存中讀數據。

  • CacheMode.REFRESH - 僅向二級緩存寫(xiě)數據,但不從二級緩存中讀數據。通過(guò) hibernate.cache.use_minimal_puts的設置,強制二級緩存從數據庫中讀取數據,刷新緩存內容。

如若需要查看二級緩存或查詢(xún)緩存區域的內容,你可以使用統計(Statistics) API。

Map cacheEntries = sessionFactory.getStatistics().getSecondLevelCacheStatistics(regionName).getEntries();

此時(shí),你必須手工打開(kāi)統計選項??蛇x的,你可以讓Hibernate更人工可讀的方式維護緩存內容。

hibernate.generate_statistics truehibernate.cache.use_structured_entries true

20.4. 查詢(xún)緩存(The Query Cache)

查詢(xún)的結果集也可以被緩存。只有當經(jīng)常使用同樣的參數進(jìn)行查詢(xún)時(shí),這才會(huì )有些用處。 要使用查詢(xún)緩存,首先你必須打開(kāi)它:

hibernate.cache.use_query_cache true

該設置將會(huì )創(chuàng )建兩個(gè)緩存區域 - 一個(gè)用于保存查詢(xún)結果集(org.hibernate.cache.StandardQueryCache); 另一個(gè)則用于保存最近查詢(xún)的一系列表的時(shí)間戳(org.hibernate.cache.UpdateTimestampsCache)。 請注意:在查詢(xún)緩存中,它并不緩存結果集中所包含的實(shí)體的確切狀態(tài);它只緩存這些實(shí)體的標識符屬性的值、以及各值類(lèi)型的結果。 所以查詢(xún)緩存通常會(huì )和二級緩存一起使用。

絕大多數的查詢(xún)并不能從查詢(xún)緩存中受益,所以Hibernate默認是不進(jìn)行查詢(xún)緩存的。如若需要進(jìn)行緩存,請調用 Query.setCacheable(true)方法。這個(gè)調用會(huì )讓查詢(xún)在執行過(guò)程中時(shí)先從緩存中查找結果, 并將自己的結果集放到緩存中去。

如果你要對查詢(xún)緩存的失效政策進(jìn)行精確的控制,你必須調用Query.setCacheRegion()方法, 為每個(gè)查詢(xún)指定其命名的緩存區域。

List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger").setEntity("blogger", blogger).setMaxResults(15).setCacheable(true).setCacheRegion("frontpages").list();

如果查詢(xún)需要強行刷新其查詢(xún)緩存區域,那么你應該調用Query.setCacheMode(CacheMode.REFRESH)方法。 這對在其他進(jìn)程中修改底層數據(例如,不通過(guò)Hibernate修改數據),或對那些需要選擇性更新特定查詢(xún)結果集的情況特別有用。 這是對SessionFactory.evictQueries()的更為有效的替代方案,同樣可以清除查詢(xún)緩存區域。

20.5.  理解集合性能(Understanding Collection performance)

前面我們已經(jīng)對集合進(jìn)行了足夠的討論。本段中,我們將著(zhù)重講述集合在運行時(shí)的事宜。

20.5.1.  分類(lèi)(Taxonomy)

Hibernate定義了三種基本類(lèi)型的集合:

  • 值數據集合

  • 一對多關(guān)聯(lián)

  • 多對多關(guān)聯(lián)

這個(gè)分類(lèi)是區分了不同的表和外鍵關(guān)系類(lèi)型,但是它沒(méi)有告訴我們關(guān)系模型的所有內容。 要完全理解他們的關(guān)系結構和性能特點(diǎn),我們必須同時(shí)考慮“用于Hibernate更新或刪除集合行數據的主鍵的結構”。 因此得到了如下的分類(lèi):

  • 有序集合類(lèi)

  • 集合(sets)

  • 包(bags)

所有的有序集合類(lèi)(maps, lists, arrays)都擁有一個(gè)由<key><index>組成的主鍵。 這種情況下集合類(lèi)的更新是非常高效的——主鍵已經(jīng)被有效的索引,因此當Hibernate試圖更新或刪除一行時(shí),可以迅速找到該行數據。

集合(sets)的主鍵由<key>和其他元素字段構成。 對于有些元素類(lèi)型來(lái)說(shuō),這很低效,特別是組合元素或者大文本、大二進(jìn)制字段; 數據庫可能無(wú)法有效的對復雜的主鍵進(jìn)行索引。 另一方面,對于一對多、多對多關(guān)聯(lián),特別是合成的標識符來(lái)說(shuō),集合也可以達到同樣的高效性能。( 附注:如果你希望SchemaExport為你的<set>創(chuàng )建主鍵, 你必須把所有的字段都聲明為not-null="true"。)

<idbag>映射定義了代理鍵,因此它總是可以很高效的被更新。事實(shí)上, <idbag>擁有著(zhù)最好的性能表現。

Bag是最差的。因為bag允許重復的元素值,也沒(méi)有索引字段,因此不可能定義主鍵。 Hibernate無(wú)法判斷出重復的行。當這種集合被更改時(shí),Hibernate將會(huì )先完整地移除 (通過(guò)一個(gè)(in a single DELETE))整個(gè)集合,然后再重新創(chuàng )建整個(gè)集合。 因此Bag是非常低效的。

請注意:對于一對多關(guān)聯(lián)來(lái)說(shuō),“主鍵”很可能并不是數據庫表的物理主鍵。 但就算在此情況下,上面的分類(lèi)仍然是有用的。(它仍然反映了Hibernate在集合的各數據行中是如何進(jìn)行“定位”的。)

20.5.2.  Lists, maps 和sets用于更新效率最高

根據我們上面的討論,顯然有序集合類(lèi)型和大多數set都可以在增加、刪除、修改元素中擁有最好的性能。

可論證的是對于多對多關(guān)聯(lián)、值數據集合而言,有序集合類(lèi)比集合(set)有一個(gè)好處。因為Set的內在結構, 如果“改變”了一個(gè)元素,Hibernate并不會(huì )更新(UPDATE)這一行。 對于Set來(lái)說(shuō),只有在插入(INSERT)刪除(DELETE) 操作時(shí)“改變”才有效。再次強調:這段討論對“一對多關(guān)聯(lián)”并不適用。

注意到數組無(wú)法延遲載入,我們可以得出結論,list, map和idbags是最高效的(非反向)集合類(lèi)型,set則緊隨其后。 在Hibernate中,set應該時(shí)最通用的集合類(lèi)型,這時(shí)因為“set”的語(yǔ)義在關(guān)系模型中是最自然的。

但是,在設計良好的Hibernate領(lǐng)域模型中,我們通??梢钥吹礁嗟募鲜聦?shí)上是帶有inverse="true" 的一對多的關(guān)聯(lián)。對于這些關(guān)聯(lián),更新操作將會(huì )在多對一的這一端進(jìn)行處理。因此對于此類(lèi)情況,無(wú)需考慮其集合的更新性能。

20.5.3.  Bag和list是反向集合類(lèi)中效率最高的

在把bag扔進(jìn)水溝之前,你必須了解,在一種情況下,bag的性能(包括list)要比set高得多: 對于指明了inverse="true"的集合類(lèi)(比如說(shuō),標準的雙向的一對多關(guān)聯(lián)), 我們可以在未初始化(fetch)包元素的情況下直接向bag或list添加新元素! 這是因為Collection.add())或者Collection.addAll() 方法 對bag或者List總是返回true(這點(diǎn)與與Set不同)。因此對于下面的相同代碼來(lái)說(shuō),速度會(huì )快得多。

Parent p = (Parent) sess.load(Parent.class, id);Child c = new Child();c.setParent(p);p.getChildren().add(c);  //no need to fetch the collection!sess.flush();

20.5.4.  一次性刪除(One shot delete)

偶爾的,逐個(gè)刪除集合類(lèi)中的元素是相當低效的。Hibernate并沒(méi)那么笨, 如果你想要把整個(gè)集合都刪除(比如說(shuō)調用list.clear()),Hibernate只需要一個(gè)DELETE就搞定了。

假設我們在一個(gè)長(cháng)度為20的集合類(lèi)中新增加了一個(gè)元素,然后再刪除兩個(gè)。 Hibernate會(huì )安排一條INSERT語(yǔ)句和兩條DELETE語(yǔ)句(除非集合類(lèi)是一個(gè)bag)。 這當然是顯而易見(jiàn)的。

但是,假設我們刪除了18個(gè)數據,只剩下2個(gè),然后新增3個(gè)。則有兩種處理方式:

  • 逐一的刪除這18個(gè)數據,再新增三個(gè);

  • 刪除整個(gè)集合類(lèi)(只用一句DELETE語(yǔ)句),然后增加5個(gè)數據。

Hibernate還沒(méi)那么聰明,知道第二種選擇可能會(huì )比較快。 (也許讓Hibernate不這么聰明也是好事,否則可能會(huì )引發(fā)意外的“數據庫觸發(fā)器”之類(lèi)的問(wèn)題。)

幸運的是,你可以強制使用第二種策略。你需要取消原來(lái)的整個(gè)集合類(lèi)(解除其引用), 然后再返回一個(gè)新的實(shí)例化的集合類(lèi),只包含需要的元素。有些時(shí)候這是非常有用的。

顯然,一次性刪除并不適用于被映射為inverse="true"的集合。

20.6.  監測性能(Monitoring performance)

沒(méi)有監測和性能參數而進(jìn)行優(yōu)化是毫無(wú)意義的。Hibernate為其內部操作提供了一系列的示意圖,因此可以從 每個(gè)SessionFactory抓取其統計數據。

20.6.1.  監測SessionFactory

你可以有兩種方式訪(fǎng)問(wèn)SessionFactory的數據記錄,第一種就是自己直接調用 sessionFactory.getStatistics()方法讀取、顯示統計數據。

此外,如果你打開(kāi)StatisticsService MBean選項,那么Hibernate則可以使用JMX技術(shù) 發(fā)布其數據記錄。你可以讓?xiě)弥兴械?tt class="literal">SessionFactory同時(shí)共享一個(gè)MBean,也可以每個(gè) SessionFactory分配一個(gè)MBean。下面的代碼即是其演示代碼:

// MBean service registration for a specific SessionFactoryHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "myFinancialApp");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationstats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactoryserver.registerMBean(stats, on); // Register the Mbean on the server
// MBean service registration for all SessionFactory‘sHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "all");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationserver.registerMBean(stats, on); // Register the MBean on the server

TODO:仍需要說(shuō)明的是:在第一個(gè)例子中,我們直接得到和使用MBean;而在第二個(gè)例子中,在使用MBean之前 我們則需要給出SessionFactory的JNDI名,使用hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name") 得到SessionFactory,然后將MBean保存于其中。

你可以通過(guò)以下方法打開(kāi)或關(guān)閉SessionFactory的監測功能:

  • 在配置期間,將hibernate.generate_statistics設置為truefalse;

  • 在運行期間,則可以可以通過(guò)sf.getStatistics().setStatisticsEnabled(true)hibernateStatsBean.setStatisticsEnabled(true)

你也可以在程序中調用clear()方法重置統計數據,調用logSummary() 在日志中記錄(info級別)其總結。

20.6.2.  數據記錄(Metrics)

Hibernate提供了一系列數據記錄,其記錄的內容包括從最基本的信息到與具體場(chǎng)景的特殊信息。所有的測量值都可以由 Statistics接口進(jìn)行訪(fǎng)問(wèn),主要分為三類(lèi):

  • 使用Session的普通數據記錄,例如打開(kāi)的Session的個(gè)數、取得的JDBC的連接數等;

  • 實(shí)體、集合、查詢(xún)、緩存等內容的統一數據記錄

  • 和具體實(shí)體、集合、查詢(xún)、緩存相關(guān)的詳細數據記錄

例如:你可以檢查緩存的命中成功次數,緩存的命中失敗次數,實(shí)體、集合和查詢(xún)的使用概率,查詢(xún)的平均時(shí)間等。請注意 Java中時(shí)間的近似精度是毫秒。Hibernate的數據精度和具體的JVM有關(guān),在有些平臺上其精度甚至只能精確到10秒。

你可以直接使用getter方法得到全局數據記錄(例如,和具體的實(shí)體、集合、緩存區無(wú)關(guān)的數據),你也可以在具體查詢(xún)中通過(guò)標記實(shí)體名、 或HQL、SQL語(yǔ)句得到某實(shí)體的數據記錄。請參考Statistics、EntityStatistics、 CollectionStatistics、SecondLevelCacheStatistics、 和QueryStatistics的API文檔以抓取更多信息。下面的代碼則是個(gè)簡(jiǎn)單的例子:

Statistics stats = HibernateUtil.sessionFactory.getStatistics();double queryCacheHitCount  = stats.getQueryCacheHitCount();double queryCacheMissCount = stats.getQueryCacheMissCount();double queryCacheHitRatio =queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);log.info("Query Hit ratio:" + queryCacheHitRatio);EntityStatistics entityStats =stats.getEntityStatistics( Cat.class.getName() );long changes =entityStats.getInsertCount()+ entityStats.getUpdateCount()+ entityStats.getDeleteCount();log.info(Cat.class.getName() + " changed " + changes + "times"  );

如果你想得到所有實(shí)體、集合、查詢(xún)和緩存區的數據,你可以通過(guò)以下方法獲得實(shí)體、集合、查詢(xún)和緩存區列表: getQueries()、getEntityNames()、 getCollectionRoleNames()getSecondLevelCacheRegionNames()。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
理解Hibernate中PO的代理類(lèi)
Hibernate持久化對象的生命周期
操縱持久化對象
Hibernate 筆記5 load和get方法的區別
Hibernate中g(shù)et()與load()的區別
Hibernate中g(shù)et方法和load方法的區別
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久