Session接口是Hibernate向應(yīng)用程序提供的操縱數(shù)據(jù)庫的主要接口癌幕,它提供了基本的保存,更新臭杰,刪除和加載Java對象的方法谚中。
Session具有一個具體的緩存,位于緩存中的對象稱為持久化對象宪塔。它和數(shù)據(jù)庫中的記錄對應(yīng)。Session可以在某些時間點比搭,按照緩存中的對象變化來執(zhí)行相應(yīng)的SQL語句南誊,同步更新數(shù)據(jù)庫,這一過程稱為刷新緩存(flush)抄囚。
站在持久化的角度將Hibernate分為四種狀態(tài):
- 持久化狀態(tài)
- 臨時狀態(tài)
- 游離狀態(tài)
- 刪除狀態(tài)
Session中的特定方法能使對象從一個狀態(tài)轉(zhuǎn)為另一個狀態(tài)。
Session緩存
在Session接口的實現(xiàn)中包含了一系列的Java集合穴亏,這些Java集合構(gòu)成了Session緩存重挑。只要Session實例沒有結(jié)束生命周期,且沒有清理緩存谬哀。則存放在它緩存的對象也不會結(jié)束生命周期。
Session緩存可以減少Hibernate應(yīng)用程序訪問數(shù)據(jù)庫的次數(shù)谦屑。
HelloWorld helloWorld = this.session.get(HelloWorld.class, 1);
HelloWorld helloWorld2 = this.session.get(HelloWorld.class, 1);
這兩條查詢只會使用一次SQL語句劲室。
HelloWorld helloWorld = session.get(HelloWorld.class, 1);
helloWorld.setTitle("Today");
當(dāng)我們使用set方法是自動更新到了數(shù)據(jù)庫。查看控制臺我們會發(fā)現(xiàn)程序?qū)嶋H上調(diào)用了SQL語句充蓝。此時會自動調(diào)用flush方法喉磁。
flush緩存
flush的作用:使數(shù)據(jù)庫中的對象與Session中的對象保持一致。為了保持一致协怒,則可能發(fā)送SQL語句。
在調(diào)用commit方法時仑撞,會先進行flush,然后再提交事務(wù)隧哮。
在Transaction的commit方法中,先調(diào)用session的flush方法陨帆,再提交事務(wù)采蚀。
flush方法可能會發(fā)送SQL語句。不會提交事務(wù)。
注意:在未提交事務(wù)或者顯示的調(diào)用session.flush()方法之前春霍,也有可能會進行flush操作。
- 執(zhí)行HQL或者QBC查詢。會先進行flush操作,得到數(shù)據(jù)表最新的記錄。
- 若數(shù)據(jù)記錄的ID是由底層數(shù)據(jù)庫使用自增的方式生成的灾螃,則在調(diào)用save方法后就會立即發(fā)送insert語句。因為save方法必須保證對象的ID是存在的嵌赠。
commit和flush方法的區(qū)別:flush執(zhí)行一系列的SQL語句熄赡,但不提交事務(wù)。commit方法會先調(diào)用flush方法彼硫,然后提交事務(wù)。意味著提交事務(wù)那么數(shù)據(jù)庫操作就被永久的保存下來词渤。
refresh
緩存中的數(shù)據(jù)是最新的狀態(tài)串绩。調(diào)用這個方法會強制發(fā)送一條select語句得到記錄的最新狀態(tài)缺虐。
refresh會強制發(fā)送select語句礁凡,使session緩存中的對象的狀態(tài)和數(shù)據(jù)表中對應(yīng)的記錄保持一致。
操作Session緩存
持久化對象
臨時對象(Transient)
在使用代理主鍵的情況下腰涧,OID通常為null紊浩。
不處于Session緩存中疗锐。
在數(shù)據(jù)庫中沒有對應(yīng)的記錄。持久化對象(Persist)
持久化對象也叫托管滑臊。
OID不為null。
位于Session緩存中雇卷。
若在數(shù)據(jù)庫中已經(jīng)有和其對應(yīng)的記錄,持久化對象和數(shù)據(jù)庫中相關(guān)的記錄對應(yīng)小染。
Session在flush緩存時贮折,會根據(jù)持久化對象的屬性變化,來同步更新數(shù)據(jù)庫调榄。
在同一個Session實例的緩存中,數(shù)據(jù)庫表中的每條記錄只對應(yīng)唯一的持久化對象筐带。刪除對象(Removed)
在數(shù)據(jù)庫中沒有和其OID對應(yīng)的記錄缤灵。
不再處于Session的緩存中。
一般情況下凤价,應(yīng)用程序不該再使用被刪除的對象。-
游離對象(Detached)
也叫托管富蓄。
OID不為null慢逾。
不再處于Session緩存中灭红。
一般情況下口注,游離對象是從持久化對象轉(zhuǎn)變過來的。因此在數(shù)據(jù)庫中可能還存在與它對應(yīng)的記錄寝志。
對象狀態(tài)轉(zhuǎn)換圖
Session持久化操作的方法
首先說一個概念OID材部。Hibernate通過持久化對象的OID來維持它和數(shù)據(jù)庫相關(guān)記錄的對應(yīng)關(guān)系。當(dāng)News對象處于持久化狀態(tài)時乐导,不允許程序隨意修改它的Id。
save()方法:
- 使一個臨時對象變?yōu)橐粋€持久化對象旺拉。
- 為對象分配Id。
- 在flush緩存時會發(fā)送一條insert語句蛾狗。
- 在save方法之前的Id是無效的泽本。
- 持久化對象的Id是不能被修改的。
persist()方法:
- 也會執(zhí)行insert操作规丽。
- 如果在persist方法之前有Id了,則不會執(zhí)行insert操作冰抢,而是拋出一個異常艘狭。
get和load的區(qū)別:
- 執(zhí)行g(shù)et方法會立即加載對象,而執(zhí)行l(wèi)oad方法巢音,若不使用該對象。則不會立即執(zhí)行查詢操作梧躺,而返回一個代理對象傲绣。get立即查詢巩踏。load延遲加載续搀。
- 如果在數(shù)據(jù)庫中沒有對應(yīng)的記錄。Session沒有被關(guān)閉禁舷,同時需要使用對象時,get會返回一個空值(null)在讶,load則會拋出一個異常霜大。
- load方法可能會拋出懶加載異常(LazyInitializationException)革答。在需要初始化代理對象之前已經(jīng)關(guān)閉了Session。
update方法:
- 若更新一個持久化對象残拐,則不需要顯式的調(diào)用update方法。因為在調(diào)用transaction的commit方法時會先執(zhí)行Session的flush方法囊卜。
- 更新一個游離對象時错沃,需要顯示的調(diào)用Session的update方法。同時可以將一個游離對象變?yōu)橐粋€持久化對象枢析。
注意:
無論要更新的游離對象與數(shù)據(jù)包中的記錄是否一致,都會發(fā)送Update語句司浪。
如何讓update方法不盲目的觸發(fā)Update語句:
在*.hbm.xml文件的class結(jié)點設(shè)置一個屬性select-before-update=true把沼。通常不需要設(shè)置該屬性。若數(shù)據(jù)表中沒有對應(yīng)的記錄饮睬,但還是調(diào)用了update方法則會拋出異常。
若在Session緩存中存在相同的OID的持久化對象垦垂,則會拋出異常。因為在同一個Session的緩存中不能有兩個Id相同的對象劫拗。
saveOrUpdate方法:
saveOrUpdate方法同時包含了save方法和update方法的功能。
判定對象為臨時對象的標(biāo)準(zhǔn):
- Java對象的OID是否為null憔足。
- 映射文件中<id>設(shè)置了unsaved-value屬性酒繁。并且Java對象的OID取值和這個unsaved-value屬性相匹配。
若OID不為空揭绑,但數(shù)據(jù)表中還沒有和其對應(yīng)的記錄郎哭,會拋出一個異常他匪。
OID的值等于ID的unsaved-value屬性值的對象夸研,也被認(rèn)為是一個游離對象。
delete方法
執(zhí)行刪除操作悼沈。只要OID和數(shù)據(jù)表中的一條記錄對應(yīng)姐扮,就會執(zhí)行delete操作。如果沒有對應(yīng)的記錄則拋出異常溶握。
可以通過設(shè)置Hibernate配置文件的屬性hibernate.use_identifier_rollback為true使刪除對象后把其OID置為null。
evict方法
從Session緩存中把指定的持久化對象移除萍肆。
通過Hibernate調(diào)用存儲過程
Session的doWork(Work)方法用于執(zhí)行Work對象指定的操作胀屿。即調(diào)用Work對象的execute方法,Session會把當(dāng)前使用的數(shù)據(jù)庫連接傳遞給execute方法宿崭。
Hibernate與觸發(fā)器協(xié)同工作
Hibernate與觸發(fā)器協(xié)同工作時會造成兩個問題:
- 觸發(fā)器使Session緩存中的持久化對象與數(shù)據(jù)庫中對應(yīng)的數(shù)據(jù)不一致。觸發(fā)器在數(shù)據(jù)庫中操作奖蔓,它執(zhí)行的操作對于Session是透明的。
- Session的update方法盲目的觸發(fā)觸發(fā)器厨疙。無論游離對象的屬性是否發(fā)生變化都會執(zhí)行update語句疑务。而update語句會觸發(fā)數(shù)據(jù)庫中相應(yīng)的觸發(fā)器。
解決方案
- 在執(zhí)行完Session的相關(guān)操作后知允,立即調(diào)用Session的flush和refresh方法。迫使Session緩存和數(shù)據(jù)庫的數(shù)據(jù)同步保屯。方法重新從數(shù)據(jù)庫中加載數(shù)據(jù)涤垫。
- 在映射文件的<class>元素中設(shè)置select-before-update屬性。當(dāng)Session的update或saveOrUpdate方法更新一個游離對象時雹姊,先執(zhí)行select語句獲取該游離對象在數(shù)據(jù)庫中的最新數(shù)據(jù)衡楞。只有在不一致的情況下才會執(zhí)行update操作。