Hibernate 中的常見(jiàn)坑
1. 前言
框架方便了開(kāi)發(fā)者的工作,但是運(yùn)行速度并沒(méi)有原生的快。原生開(kāi)發(fā)是直接告訴 JVM 要做什么,框架則是曲線救國(guó),JVM 需要繞些圈子才明白開(kāi)發(fā)者的意圖。
顯然,框架是要以更多性能消耗、運(yùn)行速度作為代價(jià)的。
本節(jié)課,和大家聊一聊使用 Hibernate 中可能遇到的坑。避開(kāi)這些坑,真正享受 Hibernate 帶給開(kāi)發(fā)者的福利。
2. 常見(jiàn)的坑
2.1 延遲加載
延遲加載是 Hibernate 提供的一種性能優(yōu)化方案,但是,使用時(shí)需要注意,必須保持在 Session 生命周期之內(nèi)。
Hibernate 中的延遲加載有 2 種情形,
- 如 load()方法。直接調(diào)用 load()方法只會(huì)產(chǎn)生一個(gè)代理對(duì)象,只有當(dāng)訪問(wèn)屬性的 get()方法時(shí),才會(huì)訪問(wèn)數(shù)據(jù)。
- one-to-many、many-to-one、many-to-many 中也可以設(shè)置延遲加載,只有訪問(wèn)與當(dāng)前查詢相關(guān)聯(lián)的關(guān)系表中的數(shù)據(jù)時(shí),才會(huì)進(jìn)行數(shù)據(jù)加載。
因?yàn)?Session 生命周期較短,如果要跨組件使用延遲加載功能, 則需保持 Session 生命周期與請(qǐng)求過(guò)程相同。WEB 項(xiàng)目開(kāi)發(fā)中,Hibernate 提供有 OpenSessionInViewFilter 過(guò)濾器,用來(lái)保證請(qǐng)求開(kāi)始和響應(yīng)結(jié)束的時(shí)候,使用同一個(gè) Session。請(qǐng)求開(kāi)始創(chuàng)建 Session,響應(yīng)結(jié)束關(guān)閉 Session。
使用時(shí),這里面會(huì)有 1 個(gè)坑:不能使用重定向,因?yàn)橹囟ㄏ蛏媳举|(zhì)上是兩次請(qǐng)求。
2.2 save ()與 merge()
這兩個(gè)方法都可以接收對(duì)象作為參數(shù),但是,save()會(huì)把傳入的對(duì)象轉(zhuǎn)換成持久化狀態(tài) ,且返回保存成功后數(shù)據(jù)的主鍵。
merge()會(huì)返回一個(gè)對(duì)象,返回的此對(duì)象才具有持久化狀態(tài)。
這里的坑就是千萬(wàn)別誤以為傳遞給 merge()方法的對(duì)象具有持久化狀態(tài)。
2.3 持久化狀態(tài)
持久化狀態(tài)的 PO 對(duì)象具有同步數(shù)據(jù)庫(kù)的能力,但是,請(qǐng)注意,持久化狀態(tài)只能是在 Session 生命周期之內(nèi)?,F(xiàn)在的項(xiàng)目都是采用的分層體系結(jié)構(gòu)。所以,不要指望業(yè)務(wù)組件從數(shù)據(jù)層組件中獲取的對(duì)象還具有持久化能力,除非你不關(guān)閉 Session。
這里的坑就是,持久化狀態(tài)的對(duì)象是有時(shí)效性的。
2.4 序列化的
從代碼規(guī)范上講,持久化對(duì)象是一定要實(shí)現(xiàn)序列化接口的,保證能在異構(gòu)化系統(tǒng)或網(wǎng)絡(luò)中進(jìn)行數(shù)據(jù)傳輸。
這里有坑就是實(shí)現(xiàn)序列化一定要成為持久化對(duì)象的標(biāo)配。
2.5 級(jí)聯(lián)操作
Hibernate 提供的級(jí)聯(lián)操作帶來(lái)了很多方便。
但是,特別是在雙向關(guān)聯(lián)映射的情況下,不要把兩邊的級(jí)聯(lián)操作全部打開(kāi),否則會(huì)把不該刪除的數(shù)據(jù)刪除掉。
測(cè)試數(shù)據(jù)被級(jí)聯(lián)刪除倒無(wú)所謂,真實(shí)數(shù)據(jù)被刪除了,可能就欲哭無(wú)門。
2.6 緩存的使用
Session 級(jí)別緩存的應(yīng)用價(jià)值不大,使用 Session 操作完畢后,盡可能進(jìn)行緩存清理。
SessionFactory 二級(jí)緩存需要打開(kāi)后才能使用,而且一般是依賴第三方插件。緩存可以提高查詢數(shù)據(jù)的速度,但是緩存本身需要消耗資源,所以,緩存的使用需要酌情考慮。
這里有坑就是不要濫用緩存,需要緩存的對(duì)象也是需要特別指定的。如此繁瑣,相必 HIbernate 也怕你濫用緩存。
2.7 更新和刪除
更新和刪除之前,一定要先從數(shù)據(jù)庫(kù)中查找到對(duì)應(yīng)數(shù)據(jù)。更新和刪除都是針對(duì)于數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
這個(gè)坑,經(jīng)常發(fā)生在我面授的學(xué)生中。
2.8 和其它框架整合
Hibernate 只是解決 JDBC 的問(wèn)題,在項(xiàng)目中,不可避免的需要和其它框架進(jìn)行整合。如 Spring 之流,這里面會(huì)有一個(gè)很大的坑,就是版本兼容問(wèn)題。
典型的表現(xiàn)就是明明添加了相應(yīng)的包,但是,出現(xiàn) class not found。這時(shí)就要考慮是否是版本之間的兼容問(wèn)題。
這個(gè)坑有的時(shí)候很讓人迷惑,明明剛剛添加了相應(yīng)的包,卻告知不存在相應(yīng)的類。
Spring Boot 提供了 DATA JPA 實(shí)現(xiàn),本質(zhì)上是使用 Hibernate 的持久化功能。Spring Boot 開(kāi)箱即用,很好解決了版本的兼容性問(wèn)題。建議大家使用。
2.9 hbm2ddl 功能
Hibernate 的 hbm2ddl 功能非常強(qiáng)大,使用時(shí)一定要注意,什么時(shí)候設(shè)置為 create,什么時(shí)候設(shè)置為 update。null
hbm2ddl 功能只用于測(cè)試、學(xué)習(xí) Hibernate 時(shí),強(qiáng)烈建議不要使用于正式環(huán)境,不要指望利用它進(jìn)行數(shù)據(jù)庫(kù)設(shè)計(jì)。
就怕初學(xué)者一看有這么好用的東西,就不管不顧任何時(shí)候都使用。
3. 小結(jié)
所謂坑,就是你我所走過(guò)的路。我遇到的你不一定會(huì)遇到,你遇到的我也不一定會(huì)碰到。所以,沒(méi)有任何一個(gè)人能告訴你所有。本課程是引路人,走完所有的路程還是要靠自己。
使用 Hibernate 時(shí),總會(huì)出現(xiàn)這樣或那樣的問(wèn)題,但是,要相信問(wèn)題肯定能得到解決。本節(jié)課通過(guò)一些常見(jiàn)問(wèn)題的解析,讓大家少走彎路。
經(jīng)驗(yàn)還是需要大家自己積累,祝好運(yùn)。