Session.load/get方法均可以根據(jù)指定的實(shí)體類(lèi)和id從數(shù)據(jù)庫(kù)讀取記錄,并返回與之對(duì)應(yīng)的實(shí)體對(duì)象。
其區(qū)別在于:
如果未能發(fā)現(xiàn)符合條件的記錄,get方法返回null,而load方法會(huì)拋出一個(gè)ObjectNotFoundException。
Load方法可返回實(shí)體的代理類(lèi)實(shí)例,而get方法永遠(yuǎn)直接返回實(shí)體類(lèi)。
load方法可以充分利用內(nèi)部緩存和二級(jí)緩存中的現(xiàn)有數(shù)據(jù),而get方法則僅僅在內(nèi)部緩存中進(jìn)行數(shù)據(jù)查找,如沒(méi)有發(fā)現(xiàn)對(duì)應(yīng)數(shù)據(jù),將越過(guò)二級(jí)緩存,直接調(diào)用SQL完成數(shù)據(jù)讀取。
Session在加載實(shí)體對(duì)象時(shí),將經(jīng)過(guò)的過(guò)程:
首先,Hibernate中維持了兩級(jí)緩存。第一級(jí)緩存由Session實(shí)例維護(hù),其中保持了Session當(dāng)前所有關(guān)聯(lián)實(shí)體的數(shù)據(jù),也稱(chēng)為內(nèi)部緩存。而第二級(jí)緩存則存在于SessionFactory層次,由當(dāng)前所有由本SessionFactory構(gòu)造的Session實(shí)例共享。出于性能考慮,避免無(wú)謂的數(shù)據(jù)庫(kù)訪(fǎng)問(wèn),Session在調(diào)用數(shù)據(jù)庫(kù)查詢(xún)功能之前,會(huì)先在緩存中進(jìn)行查詢(xún)。首先在第一級(jí)緩存中,通過(guò)實(shí)體類(lèi)型和id進(jìn)行查找,如果第一級(jí)緩存查找命中,且數(shù)據(jù)狀態(tài)合法,則直接返回。
之后,Session會(huì)在當(dāng)前“NonExists(把無(wú)效的條件寫(xiě)成一個(gè)黑名單,既然無(wú)效,那么也沒(méi)必要再查下去)”記錄中進(jìn)行查找,如果“NonExists”記錄中存在同樣的查詢(xún)條件,則返回null。“NonExists”記錄了當(dāng)前Session實(shí)例在之前所有查詢(xún)操作中,未能查詢(xún)到有效數(shù)據(jù)的查詢(xún)條件(相當(dāng)于一個(gè)查詢(xún)黑名單列表)。如此一來(lái),如果Session中一個(gè)無(wú)效的查詢(xún)條件重復(fù)出現(xiàn),即可迅速作出判斷,從而獲得最佳的性能表現(xiàn)。
對(duì)于load方法而言,如果內(nèi)部緩存中未發(fā)現(xiàn)有效數(shù)據(jù),則查詢(xún)第二級(jí)緩存,如果第二級(jí)緩存命中,則返回。
如在緩存中未發(fā)現(xiàn)有效數(shù)據(jù),則發(fā)起數(shù)據(jù)庫(kù)查詢(xún)操作(Select SQL),如經(jīng)過(guò)查詢(xún)未發(fā)現(xiàn)對(duì)應(yīng)記錄,則將此次查詢(xún)的信息在“NonExists”中加以記錄,并返回null。
根據(jù)映射配置和Select SQL得到的ResultSet,創(chuàng)建對(duì)應(yīng)的數(shù)據(jù)對(duì)象。
將其數(shù)據(jù)對(duì)象納入當(dāng)前Session實(shí)體管理容器(一級(jí)緩存)。
執(zhí)行Interceptor.onLoad方法(如果有對(duì)應(yīng)的Interceptor)。
將數(shù)據(jù)對(duì)象納入二級(jí)緩存。
如果數(shù)據(jù)對(duì)象實(shí)現(xiàn)了LifeCycle接口,則調(diào)用數(shù)據(jù)對(duì)象的onLoad方法。
返回?cái)?shù)據(jù)對(duì)象。
/** *//**
* load()方法的執(zhí)行順序如下:
* a):首先通過(guò)id在session緩存中查找對(duì)象,如果存在此id的對(duì)象,直接將其返回
* b):在二級(jí)緩存中查找,找到后將 其返回。
* c):如果在session緩存和二級(jí)緩存中都找不到此對(duì)象,則從數(shù)據(jù)庫(kù)中加載有此ID的對(duì)象
* 因此load()方法并不總是導(dǎo)致SQL語(yǔ)句,只有緩存中無(wú)此數(shù)據(jù)時(shí),才向數(shù)據(jù)庫(kù)發(fā)送SQL!
*/
/** *//**
* 與get()的區(qū)別:
* 1:在立即加載對(duì)象(當(dāng)hibernate在從數(shù)據(jù)庫(kù)中取得數(shù)據(jù)組裝好一個(gè)對(duì)象后
* 會(huì)立即再?gòu)臄?shù)據(jù)庫(kù)取得數(shù)據(jù)此對(duì)象所關(guān)聯(lián)的對(duì)象)時(shí),如果對(duì)象存在,
* load()和get()方法沒(méi)有區(qū)別,都可以取得已初始化的對(duì)象;但如果當(dāng)對(duì)
* 象不存在且是立即加載時(shí),使用get()方法則返回null,而使用load()則
* 拋出一個(gè)異常。因此使用load()方法時(shí),要確認(rèn)查詢(xún)的主鍵ID一定是存在
* 的,從這一點(diǎn)講它沒(méi)有g(shù)et方便!
* 2:在延遲加載對(duì)象(Hibernate從數(shù)據(jù)庫(kù)中取得數(shù)據(jù)組裝好一個(gè)對(duì)象后,
* 不會(huì)立即再?gòu)臄?shù)據(jù)庫(kù)取得數(shù)據(jù)組裝此對(duì)象所關(guān)聯(lián)的對(duì)象,而是等到需要時(shí),
* 都會(huì)從數(shù)據(jù)庫(kù)取得數(shù)據(jù)組裝此對(duì)象關(guān)聯(lián)的對(duì)象)時(shí),get()方法仍然使用
* 立即加載的方式發(fā)送SQL語(yǔ)句,并得到已初始化的對(duì)象,而load()方法則
* 根本不發(fā)送SQL語(yǔ)句,它返回一個(gè)代理對(duì)象,直到這個(gè)對(duì)象被訪(fǎng)問(wèn)時(shí)才被
* 初始化。
*/
get()----不支持LAZY
load()----支持LAZY
load和get一共是2個(gè)區(qū)別 先講第一個(gè) 延遲加載
load是true而get是false
意 思就是 load采用的是延遲加載的方式 而get不是,hibernate思想是 既然這個(gè)方法支持延遲加載 他就認(rèn)為這個(gè)對(duì)象一定在數(shù)據(jù)庫(kù)存在,在你 聲明 TFaq tfag2=(TFaq)sess.load(TFaq.class, 300); 這句時(shí)候,hibernate就干了一件事
1.查詢(xún)session緩存
2.緩存中沒(méi)有這個(gè)對(duì)象 就創(chuàng)建個(gè)代理
因?yàn)檠舆t加載需要代理來(lái)執(zhí)行 所以就創(chuàng)建了個(gè)代理
ok 到此為止 這句話(huà)就干了個(gè)這個(gè) 并沒(méi)有去數(shù)據(jù)庫(kù)交互查詢(xún)
當(dāng)你使用這個(gè)對(duì)象 比如tfag2.getTfRtitle()或get方法時(shí)候
這個(gè)時(shí)候 hibernate就去查詢(xún)二級(jí)緩存和數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)沒(méi)有這條數(shù)據(jù) 就拋出異常
整個(gè)load方法調(diào)用結(jié)束 load沒(méi)什么神奇 這就是他干過(guò)所有的事情
load方法講完了 我在講一下get方法工作原理
因?yàn)閔ibernate規(guī)定get方法不能使用延遲加載 所以和load還是不一樣的
TFaq tfag2=(TFaq)sess.get(TFaq.class, 300);
在創(chuàng)建這條語(yǔ)句時(shí)候 我們看看hibernate干了哪些事
1.get方法首先查詢(xún)session緩存 (session緩存就是hibernate的一級(jí)緩存 這個(gè)概念大家應(yīng)該清楚吧 )
2.get方法如果在session緩存中找到了該id對(duì)應(yīng)的對(duì)象,如果剛好該對(duì)象前面是被代理過(guò)的,如被load方法使用過(guò),或者被其他關(guān)聯(lián)對(duì)象延遲加載過(guò),那么返回的還是原先的代理對(duì)象,而不是實(shí)體類(lèi)對(duì)象。
3.如果該代理對(duì)象還沒(méi)有加載實(shí)體數(shù)據(jù)(就是id以外的其他屬性數(shù)據(jù)),那么它會(huì)查詢(xún)二級(jí)緩存或者數(shù)據(jù)庫(kù)來(lái)加載數(shù)據(jù),但是返回的還是代理對(duì)象,只不過(guò)已經(jīng)加載了實(shí)體數(shù)據(jù)。
(這個(gè)代理實(shí)際就是空的對(duì)象 并沒(méi)有去數(shù)據(jù)庫(kù)查詢(xún)得到的 我們叫代理對(duì)象,如果 去數(shù)據(jù)庫(kù)查詢(xún)了 返回到了這個(gè)對(duì)象 我們叫實(shí)體對(duì)象 就是這個(gè)對(duì)象真實(shí)存在)
我在總結(jié)性一句話(huà)這2者區(qū)別
get方法首先查詢(xún)session緩存,沒(méi)有的話(huà)查詢(xún)二級(jí)緩存,最后查詢(xún)數(shù)據(jù)庫(kù);反而load方法創(chuàng)建時(shí)首先查詢(xún)session緩存,沒(méi)有就創(chuàng)建代理,實(shí)際使用數(shù)據(jù)時(shí)才查詢(xún)二級(jí)緩存和數(shù)據(jù)庫(kù)
----我測(cè)試過(guò):
在使用session.get方法后如果把session關(guān)閉的話(huà),也會(huì)出現(xiàn)懶加載異常。
那么只有在manytoone標(biāo)簽里配置 lazy="false"時(shí)異常才會(huì)解決。
也就是說(shuō)上面轉(zhuǎn)載的第3條不是那么正確:返回該代理對(duì)象不錯(cuò),但是如果該對(duì)象沒(méi)有加載實(shí)體數(shù)據(jù),那么也會(huì)在用到時(shí)才會(huì)加載,即不會(huì)立即查詢(xún)數(shù)據(jù)庫(kù)或者二級(jí)緩存,那么你現(xiàn)在把session關(guān)閉,這個(gè)對(duì)行啊沒(méi)有加載實(shí)體數(shù)據(jù)----才會(huì)出現(xiàn)懶加載異常。