關於延遲加載(lazy)和強制加載(Hibernate.initialize(Object proxy) )

PO 即Persistence Object 

VO 即Value Object 


PO 和VO 是Hibernate 中號碼大全兩個比較要害的概念。 

首要,何謂VO,很簡略,VO 即是一個簡略的值方針。 

如: 

TUser user = new TUser(); 

user.setName("Emma"); 


這兒的user 即是一個VO。VO 僅僅簡略關鍵詞挖掘工具攜帶了方針的一些特點信息。 

何謂PO? 即歸入Hibernate 辦理結構中的VO。看下面兩個比如: 

TUser user = new TUser(); 

TUser anotherUser = new TUser(); 

user.setName("Emma"); 

anotherUser.setName("Kevin"); 


//此刻user和anotherUser都是VO 


Transaction tx = session.beginTransaction(); 

session.save(user);


//此刻的user現已經過Hibernate的處置,成爲一個PO ,而anotherUser依然是個VO 


tx.commit(); 


//業務提交以後,庫表中現已刺進一條用戶”Emma”的記載,關於anotherUser則無任何操作


Transaction tx = session.beginTransaction(); 

user.setName("Emma_1"); //PO 

anotherUser.setName("Kevin_1");//VO 

tx.commit(); 


//業務提交以後,PO的狀況被固化到數據庫中,也即是說數據庫中“Emma”的用戶記載現已被更新爲“Emma_1”,此刻anotherUser依然是個通常Java方針,它的特點更改不會對數據庫產生任何影響,別的,經過Hibernate回來的方針也是PO: 由Hibernate回來的PO ,如:


TUser user = (TUser)session.load(TUser.class,new Integer(1)); 


VO經過Hibernate進行處置,就成爲了PO。上面的示例代碼session.save(user)中,咱們把一個VO “user”傳遞給Hibernate的Session.save辦法進行保留。在save辦法中,Hibernate對其進 

行如下處置: 

1. 在當時session所對應的實體容器(Entity Map)中查詢是不是存在user方針的引證。 

2. 假如引證存在,則直接回來user方針id,save進程完畢. Hibernate中,關於每個Session有一個實體容器(實踐上是一個Map方針), 假如此容器中現已保留了方針方針的引證,那麼hibernate 會以爲此方針現已與Session相有關。 

關於save操作而言,假如方針現已與Session相有關(即現已被參加Session 的實體容器中),則無需進行詳細的操作。由於以後的Session.flush 進程中,Hibernate會對此實體容器中的方針進行遍歷,查找出發作改變的實體,生成並履行相應的update句子。 

3. 假如引證不存在,則依據映射聯繫,履行insert操作。 

a) 在咱們這兒的示例中,採用了native的id生成機制,因而hibernate會從數據庫取得insert操作生成的id並賦予user方針的id特點。 

b) 將user方針的引證歸入Hibernate的實體容器。 

c) save 進程完畢,回來方針id. 

而Session.load辦法中,再回來方針之前,Hibernate 就現已將此方針歸入其實體容器中。


VO和PO的首要差異在於: 


. VO是獨立的Java Object。 

. PO是由Hibernate歸入其實體容器(EntityMap)的方針,它代表了與數 

據庫中某條記載對應的Hibernate實體,PO的改變在業務提交時將反應到實 

際數據庫中。假如一個PO與Session對應的實體容器中別離(如Session 封閉後的PO),那麼 

此刻,它又會成爲一個VO。


關於unsaved-value 


在非顯現數據保留時,Hibernate 將依據這個值來判別方針是不是需求保留。 

所謂顯式保留,是指代碼中清晰調用session 的save 、update 、saveOrupdate 辦法對方針進行持久化。如:


session.save(user); 


而在某些情況下,如映射聯繫中,Hibernate 依據級聯(Cascade )聯繫對聯接類進行保留。此刻代碼中沒有關於級聯方針的顯現保留句子,需求Hibernate 依據方針當時狀 

態判別是不是需求保留到數據庫。此刻,Hibernate 行將依據unsaved-value 進行斷定。 

首要Hibernate 會取出方針方針的id。以後,將此值與unsaved-value 進行比對,假如相等,則以爲方針方針沒有保留,不然,以爲方針現已保留,無需再進行保留操作。 

如:user 方針是之前由hibernate 從數據庫中獲取,一起,此user 方針的若干個有關方針address 也被加載,此刻咱們向user 方針新增一個address 方針,此刻調用 

session.save(user),hibernate 會依據unsaved-value 判別user 方針的數個address 

有關方針中,哪些需求履行save 操作,而哪些不需求。


關於咱們新參加的address 方針而言,由於其id(Integer 型)沒有賦值,因而爲null,與咱們設定的unsaved-value(null )相同,因而hibernate 將其視爲一個未保留方針,將爲其生成insert 句子並履行。


這兒能夠會產生一個疑問,假如“原有”有關方針發作改變(如user 的某個“原有” 的address 方針的特點發作了改變,所謂“原有”即此address 方針現已與user 相有關,而不是咱們在此進程中爲之新增的),此刻id 值是從數據庫中讀出,並沒有發作改變,天然 

與unsaved-value(null)也不一樣,那麼Hibernate 是不是就不保留了? 


上面關於PO、VO 的評論中從前涉及到數據保留的疑問,實踐上,這兒的“保留”, 實踐上是“insert”的概念,僅僅關於新有關方針的參加,而非數據庫華夏有有關方針的 

“update”。所謂新有關方針,通常情況下能夠理解爲未與Session 發作有關的VO。而“原有”有關方針,則是PO。: 


關於save操作而言,假如方針現已與Session相有關(即現已被參加Session的實體容器中),則無需進行詳細的操作。由於以後的Session.flush 進程中,Hibernate 

會對此實體容器中的方針進行遍歷,查找出發作改變的實體,生成並履行相應的update 句子。


Inverse和Cascade 


Inverse,直譯爲“迴轉”。在Hibernate語義中,Inverse 指定了相相聯繫中的方向。相相聯繫中,inverse=”false”的爲主動方,由主動方擔任保護相相聯繫。詳細可參見一對多聯繫中的描繪。


而Cascade,譯爲“級聯”,標明方針的級聯聯繫,如TUser 的Cascade設爲all, 就標明假如發作對user方針的操作,需求對user所有關的方針也進行相同的操作。如對user方針履行save操作,則有必要對user方針相有關的address也履行save操作。


初學者常常混雜inverse和cascade,實踐上,這是兩個互不有關的概念。Inverse 指的是相相聯繫的操控方向,而cascade指的是層級之間的連鎖操作。


推遲加載(Lazy Loading) 


爲了防止一些情況下,相相聯繫所帶來的無謂的功用開支。Hibernate引入了推遲加載的概念。如,示例中user方針在加載的時分,會一起讀取其所有關的多個地址(address)方針, 

關於需求對address進行操作的應用邏輯而言,有關數據的主動加載機制確實非常有用。可是,假如咱們僅僅想要取得user的性別(sex)特點,而不關心user的地址(address) 

信息,那麼主動加載address的特性就顯得剩餘,而且造成了極大的功用浪費。爲了取得user 的性別特點,咱們能夠還要一起從數據庫中讀取數條無用的地址數據,這致使了很多無謂的體系開支。


推遲加載特性的呈現,恰是爲了處理這個疑問。所謂推遲加載,即是在需求數據的時分,才真實履行數據加載操作。


關於咱們這兒的user方針的加載進程,也就意味着,加載user方針時只關於其自身的特點, 而當咱們需求獲取user方針所有關的address信息時(如履行user.getAddresses時),才 

真實從數據庫中加載address數據並回來。 

測驗履行以下代碼:


Criteria criteria = session.createCriteria(TUser.class); 


criteria.add(Expression.eq("name","Erica")); 

List userList = criteria.list(); 

-        indexRead arguments from command-line "http://www.shoudashou.com"

-        indexRead arguments from command-line "http://www.4lunwen.cn"

-        indexRead arguments from command-line "http://www.zx1234.cn"

-        indexRead arguments from command-line "http://www.penbar.cn"

-        indexRead arguments from command-line "http://www.whathappy.cn"

-        indexRead arguments from command-line "http://www.lunjin.net"

-        indexRead arguments from command-line "http://www.ssstyle.cn"

-        indexRead arguments from command-line "http://www.91fish.cn"

-        indexRead arguments from command-line "http://www.fanselang.com"

TUser user =(TUser)userList.get(0); 


System.out.println("User name => "+user.getName()); 

Set hset = user.getAddresses(); 


session.close();//封閉Session


TAddress addr = (TAddress)hset.toArray()[0]; 

System.out.println(addr.getAddress()); 


運行時拋出反常: 

LazyInitializationException - Failed to lazily initialize a collection - no session or session was closed 


假如咱們稍做調整,將session.close放在代碼結尾,則不會發作這樣的疑問。這意味着,只要咱們實踐加載user 有關的address時,Hibernate 才企圖經過 

session從數據庫中加載實踐的數據集,而由於咱們讀取address之前現已封閉了session,所以報出session已封閉的過錯。


這兒有個疑問,假如咱們採用了推遲加載機制,但期望在一些情況下,完成非推遲加載時的功用,也即是說,咱們期望在Session封閉後,依然答應操作user的addresses 

特點。如,爲了向View層供給數據,咱們有必要供給一個完好的User方針,包含其所有關的address信息,而這個User方針有必要在Session封閉以後依然能夠運用。


Hibernate.initialize辦法能夠經過強行加載有關方針完成這一功用: 


Hibernate.initialize(user.getAddresses()); 

session.close(); 

//經過Hibernate.initialize辦法強行讀取數據 

//addresses方針即可脫離session進行操作


Set hset= user.getAddresses(); 

TAddress addr = (TAddress)hset.toArray()[0]; 

System.out.println(addr.getAddress()); 


爲了完成透明化的推遲加載機制,hibernate進行了很多努力。其中包含JDK Collection接口的獨立完成。 

假如咱們測驗用HashSet強行轉化Hibernate回來的Set 型方針: 


Set hset = (HashSet)user.getAddresses(); 


就會在運行期得到一個java.lang.ClassCastException, 實踐上,此刻回來的是一個Hibernate的特定Set完成“net.sf.hibernate.collection.Set”方針,而非 

傳統意義上的JDK Set完成。這也恰是咱們爲何在編寫POJO時,有必要用JDKCollection 接口(如Set,Map), 而非特定的JDKCollection 完成類(如HashSet、HashMap)申明Collection特點的緣由。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章