Hibernate 延迟加载(一)
1 延遲加載策略
Hibernate 的延遲加載(lazy load)是一個(gè)被廣泛使用的技術(shù)。這種延遲加載保證了應(yīng)用只有在需要時(shí)才去數(shù)據(jù)庫中抓取相應(yīng)的記錄。通過延遲加載技術(shù)可以避免過多、過早地加載數(shù)據(jù)表里的數(shù)據(jù),從而降低應(yīng)用的內(nèi)存開銷。
Hibernate 的延遲加載本質(zhì)上就是代理模式的應(yīng)用,當(dāng)程序通過 Hibernate 裝載一個(gè)實(shí)體時(shí),默認(rèn)情況下,Hibernate 并不會(huì)立即抓取它的集合屬性、關(guān)聯(lián)實(shí)體所以對應(yīng)的記錄,而是通過生成一個(gè)代理來表示這些集合屬性、關(guān)聯(lián)實(shí)體,這就是代理模式應(yīng)用帶來的優(yōu)勢。
但是,延遲加載也是項(xiàng)目開發(fā)中特別常見的一個(gè)錯(cuò)誤。如果對一個(gè)類或者集合配置了延遲檢索策略,那么必須當(dāng)代理類實(shí)例或代理集合處于持久化狀態(tài)(即處于Session范圍內(nèi))時(shí),才能初始化它。如果在游離狀態(tài)時(shí)才初始化它,就會(huì)產(chǎn)生延遲初始化錯(cuò)誤。所以,在開發(fā)獨(dú)立的DAO數(shù)據(jù)訪問層時(shí)應(yīng)該格外小心這個(gè)問題。
如果在獲取對象的時(shí)候使用的是session.get()是不會(huì)延遲加載的,只有在使用load、hql時(shí)候才會(huì)延遲加載。
? ? ? Hibernate中允許使用延遲加載的地方主要有以下幾個(gè)地方:
<hibernate-mapping default-lazy=(true|false)”true”>:設(shè)置全局的延遲加載策略。
<class lazy=(true|false)>:DTD沒設(shè)置默認(rèn)值,推理默認(rèn)值為true
<property lazy=(true|false)>:設(shè)置字段延遲加載,默認(rèn)為false
<component lazy=(true|false):默認(rèn)為false
<subclass lazy=(true|false)>:默認(rèn)設(shè)置為true
<join-subclass lazy=(true|false)>:默認(rèn)設(shè)置為true
<union-subclass lazy=(true|false)>:默認(rèn)設(shè)置為true
<many-to-one lazy=(proxy|no-proxy|false)>:默認(rèn)為proxy
<one-to-one lazy=(proxy|no-proxy|false)>:默認(rèn)為proxy
<map lazy=(true|extra|false)>:默認(rèn)為true
<set lazy=(true|extra|false)>:默認(rèn)為true
<bag lazy=(true|extra|false)>:默認(rèn)為true
<ibag lazy=(true|extra|false)>:默認(rèn)為true
<list lazy=(true|extra|false)>:默認(rèn)為true
2 對象加載<class>
2.1 延遲加載策略(默認(rèn))
如果想對實(shí)體對象使用延遲加載,必須要在實(shí)體的映射配置文件中進(jìn)行相應(yīng)的配置
<class name="Person" table="PERSON" lazy="true">
1 ? ? tx = session.beginTransaction();
2 Person p=(Person) session.load(Person.class, "001");//(1)
3 ? ? System.out.println("0: "+p.getPersonId());//(2)
4 ? ? System.out.println("0: "+p.getName());//(3)
5 tx.commit();
6 ? ? session.close();
執(zhí)行到(1)并沒有出現(xiàn)sql語句,并沒有從數(shù)據(jù)庫中抓取數(shù)據(jù)。這個(gè)時(shí)候查看內(nèi)存對象p如下:
圖2.1 person對象load時(shí)的內(nèi)存快照
觀察person對象,我們可發(fā)現(xiàn)是Person$$EnhancerBy..的類型的對象。這里所返回的對象類型就是Person對象的代理對象,在hibernate中通過使用CGLB來先動(dòng)態(tài)構(gòu)造一個(gè)目標(biāo)對象的代理類對象,并且在代理對象中包含目標(biāo)對象的所有屬性和方法。所以,對于客戶端而言是否為代理類是無關(guān)緊要的,對他來說是透明的。這個(gè)對象中,僅僅設(shè)置了id屬性(即personId的值),這是為了便于后面根據(jù)這個(gè)Id從數(shù)據(jù)庫中來獲取數(shù)據(jù)。
? 運(yùn)行到(2)處,輸出為001,但是仍然沒有從數(shù)據(jù)庫里面讀取數(shù)據(jù)。這個(gè)時(shí)候代理類的作用就體現(xiàn)出來了,客戶端覺得person類已經(jīng)實(shí)現(xiàn)了(事實(shí)上并未創(chuàng)建)。但是,如果這個(gè)時(shí)候session關(guān)閉,再使用person對象就會(huì)出錯(cuò)了。
? 調(diào)試運(yùn)行到(3)處,要用到name屬性,但是這個(gè)值在數(shù)據(jù)庫中。所以hibernate從數(shù)據(jù)庫里面抓取了數(shù)據(jù),sql語句如下所示:
Hibernate: ? ? select ? ? ? ?person0_.PERSONID as PERSONID3_0_, ? ? ? ?person0_.NAME as NAME3_0_ ? ? from ? ? ? ?PERSON person0_ ? ? where ? ? ? ?person0_.PERSONID=?這時(shí)候,我們查看內(nèi)存里面的對象如下:
圖2.2 class延遲加載時(shí)內(nèi)存對象
真正的Person對象放在CGLIB$CALLBACK_0對象中的target屬性里。
這樣,通過一個(gè)中間代理對象,Hibernate實(shí)現(xiàn)了實(shí)體的延遲加載,只有當(dāng)用戶真正發(fā)起獲得實(shí)體對象屬性的動(dòng)作時(shí),才真正會(huì)發(fā)起數(shù)據(jù)庫查詢操作。所以實(shí)體的延遲加載是用通過中間代理類完成的,所以只有session.load()方法才會(huì)利用實(shí)體延遲加載,因?yàn)橹挥衧ession.load()方法才會(huì)返回實(shí)體類的代理類對象。
2.2 非延遲加載策略
Hibernate默認(rèn)的策略便是非延遲加載的,所以設(shè)置lazy=false
1 ? ? tx = session.beginTransaction();2 ? ? Pe
2 Person p=(Person) session.load(Person.class, "001");//(1)
3 ? ? System.out.println("0: "+p.getPersonId());//(2)
4 ? ? System.out.println("0: "+p.getName());//(3)
5 tx.commit();6 ? ? session.close();
調(diào)試運(yùn)行到(1)處時(shí),hibernate直接執(zhí)行如下sql語句:
Hibernate: ? ? select ? ? ? ?person0_.PERSONID as PERSONID3_0_, ? ? ? ?person0_.NAME as NAME3_0_ ? ? from ? ? ? ?PERSON person0_ ? ? where ? ? ? ?person0_.PERSONID=?我們在查看內(nèi)存快照如下:
? ? ?這個(gè)時(shí)候就不是一個(gè)代理類了,而是Person對象本身了。里面的屬性也已經(jīng)全部普通屬性也全部被加載。這里說普通屬性是因?yàn)閍ddresses這個(gè)集合對象并沒有被加載,因?yàn)閟et自己本身也可以設(shè)置lazy屬性。所以,這里也反映出class對象的lazy并不能控制關(guān)聯(lián)或集合的加載策略。(普通屬性會(huì)被加載,但是集合之類的就備受控制)
2.3 總結(jié)
Hibernate中<class lazy=””>默認(rèn)為true。如果,在load的時(shí)候只會(huì)返回一個(gè)代理類,并不會(huì)正在從數(shù)據(jù)庫中讀取數(shù)據(jù)。第一次用到時(shí),會(huì)將所有普通屬性(set這種就不是)全部加載進(jìn)來。如果第一次使用到時(shí),session已經(jīng)關(guān)閉將發(fā)生錯(cuò)誤。
如果顯式是設(shè)置lazy=false,load的時(shí)候即會(huì)把所有普通屬性全部讀取進(jìn)來。而且,返回的將是一個(gè)真正的該類型的對象(如Person),而不是代理類。
3 字段加載(property)
在Hibernate3中,引入了一種新的特性——屬性的延遲加載,這個(gè)機(jī)制又為獲取高性能查詢提供了有力的工具。在大數(shù)據(jù)對象讀取時(shí),如Person對象中有一個(gè)School字段,該字段是一個(gè)java.sql.Clob類型,包含了用戶的簡歷信息,當(dāng)我們加載該對象時(shí),我們不得不每一次都要加載這個(gè)字段,而不論我們是否真的需要它,而且這種大數(shù)據(jù)對象的讀取本身會(huì)帶來很大的性能開銷。
轉(zhuǎn)載于:https://blog.51cto.com/longx/1354527
總結(jié)
以上是生活随笔為你收集整理的Hibernate 延迟加载(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS兼容各个浏览器的本地图片上传即时预览
- 下一篇: MONGOOSE – 让NODE.JS高