文章出自:http://m.tkk7.com/BlueDavy/archive/2006/03/27/37582.html
一. 序
在實(shí)際項(xiàng)目中使用Hibernate有兩年多了,在兩年多的實(shí)踐過(guò)程中既體驗(yàn)到了Hibernate帶來(lái)的N多好處,同時(shí)也碰到不少的問(wèn)題,特寫此篇文章做個(gè)總結(jié),記錄自己在Hibernate實(shí)踐中的一些經(jīng)驗(yàn),希望對(duì)于新使用Hibernate的朋友能有個(gè)幫助,避免走過(guò)多的彎路。
閱讀本文前建議至少擁有Hibernate的一些基本知識(shí),因?yàn)楸疚牟粫?huì)去詳細(xì)介紹相關(guān)的基本知識(shí),最好就是先用Hibernate開發(fā)了一個(gè)HelloWorld,^_^。
根據(jù)自己所經(jīng)歷的項(xiàng)目中使用Hibernate所涉及的范圍,本文從開發(fā)環(huán)境、開發(fā)、設(shè)計(jì)、性能、測(cè)試以及推薦的相關(guān)書籍方面進(jìn)行講述,本篇文檔不會(huì)講的非常細(xì)致,只是根據(jù)自己在實(shí)踐時(shí)的經(jīng)驗(yàn)提出一些建議,關(guān)于細(xì)致以及具體的部分請(qǐng)參閱《Hibernate Reference》或推薦的相關(guān)書籍章節(jié)。
此文檔的PDF版本請(qǐng)到此下載:
http://m.tkk7.com/Files/BlueDavy/Hibernate 實(shí)踐.rar
本文允許轉(zhuǎn)載,但轉(zhuǎn)載時(shí)請(qǐng)注明作者以及來(lái)源。
作者:BlueDavy
來(lái)源:m.tkk7.com/BlueDavy
二. 開發(fā)環(huán)境
Hibernate 開發(fā)環(huán)境的搭建非常的簡(jiǎn)單,不過(guò)為了提高基于Hibernate開發(fā)的效率,通常都需要使用一些輔助工具,如xdoclet、middlegen等。
盡管Hibernate已經(jīng)封裝提供了很簡(jiǎn)單的進(jìn)行持久的方法,但在實(shí)際項(xiàng)目的使用中基本還是要提供一些通用的代碼,以便在進(jìn)行持久的相關(guān)操作的時(shí)候能夠更加的方便。
2.1. lib
2.1.1. Hibernate lib
Hibernate 相關(guān)的 lib 自然是開發(fā)環(huán)境中首要的問(wèn)題,這部分可以從 Hibernate 的官方網(wǎng)站進(jìn)行下載,在其官方網(wǎng)站中同時(shí)提供了對(duì)于 Hibernate 所必須依賴的 lib 以及其他可選 lib 的介紹。
2.2. xdoclet
Hibernate 作為ORM工具,從名字上就能看出它需要一個(gè)從O à R 的Mapping的描述,而這個(gè)描述就是在使用Hibernate時(shí)常見的hbm.xml,在沒(méi)有工具支持的情況下,需要在編寫持久層對(duì)象的同時(shí)手寫這個(gè)文件,甚為不便。
在jdk 5.0未推出之前,xdoclet支持的在javadoc中編寫注釋生成相關(guān)配置文件的方式大受歡迎,減少了編寫hibernate映射文件的復(fù)雜性,手寫一個(gè)完整的hibernate映射文件出錯(cuò)幾率比較的高,再加上手寫容易造成編寫出來(lái)的風(fēng)格相差很大,因此,基于xdoclet來(lái)生成hbm.xml的方式被大量的采用,基于xdoclet來(lái)編寫能夠基于我們?cè)诔志脤訉?duì)象上編寫的javadoc來(lái)生成hbm.xml文件,非常的方便。
2.2.1. Hibernate template
如果沒(méi)記錯(cuò)的話,大概在 04 年的時(shí)候 javaeye 上有位同仁整理了一個(gè)這樣的 template 文件, ^_^ ,非常感謝,我一直都在用著,呵呵。
這個(gè)文件的方便就是把它導(dǎo)入 eclipse 后,在 javadoc 中我們可以直接寫 hibid ,然后按 eclipse 的代碼輔助鍵 (alt+/) 來(lái)生成整個(gè) hibernate.id 的相關(guān)的格式,呵呵,免得在寫 hibernate.id 這些東西的時(shí)候過(guò)于麻煩, ^_^ ,這個(gè) template 文件我稍微做了修改,可在這里下載:
http://m.tkk7.com/Files/BlueDavy/templates-eclipse-tags.rar
當(dāng)然,你也可以選擇直接用 xdoclet 提供的 template 文件,不過(guò) xdoclet 官方網(wǎng)站上好像只提供了可直接導(dǎo)入 idea 的模板文件。
關(guān)于注釋上的 hibernate.id 這些東西具體請(qǐng)參見 xdoclet 官方網(wǎng)站的說(shuō)明。
如果你的項(xiàng)目采用的是 jdk 5 ,那么就可以直接使用 hibernate annotation 了,那就更為方便。
2.2.2. Ant task build
Eclipse 里沒(méi)有集成 xdoclet 的插件,你也可以去安裝一個(gè) jboss ide 的插件,里面有 xdoclet 的插件,反正我是覺得太麻煩了。
在項(xiàng)目中我仍然采用 ant task 的方式來(lái)生成 hbm.xml , target 如下所示:
<path id="app.classpath">
<pathelement path="${java.class.path}"/>
<fileset dir="${xdoclib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="hbm" description=" 生成映射文件 ">
<tstamp>
<format property="TODAY" pattern="yy-MM-dd"/>
</tstamp>
<taskdef name="hibernatedoclet" classname="xdoclet.modules.hibernate.HibernateDocletTask" classpathref="app.classpath"/>
<hibernatedoclet destdir="src/java" force="true" verbose="true" excludedtags="@version,@author,@todo">
<fileset dir="src/java">
<include name="**/po/**/*.java"/>
</fileset>
<hibernate version ="3.0"/>
</hibernatedoclet>
</target>
這個(gè)文件請(qǐng)根據(jù)項(xiàng)目情況以及環(huán)境稍做修改, ^_^ ,其中需要通過(guò) properties 文件指明 xdocletlib.dir ,類似 xdocletlib.dir=c:\xdocletlib ,里面放置 xdoclet 的相關(guān) jar 文件。
在搭建好了這樣的環(huán)境后,就可以在直接在 eclipse 中運(yùn)行 ant 文件中的這個(gè) target 來(lái)生成 hbm.xml 。
2.3. Hibernate3 Tools
如果采用Hibernate 3,則可以直接下載Hibernate 3 Tools的Eclipse Plugin,那就可以類似在PL/SQL里執(zhí)行sql一樣在eclipse里執(zhí)行hql,^_^
2.4. HibernateUtil
為了方便項(xiàng)目中Hibernate的使用,一般來(lái)說(shuō)都會(huì)提供HibernateUtil這樣的類,這個(gè)類的作用主要是創(chuàng)建sessionFactory和管理session,在Hibernate 3以前采用的是在這里建立ThreadLocal來(lái)存放session,在Hibernate 3以后則可以直接使用SessionFactory.getCurrentSession來(lái)獲取session,而session的獲取方式則可通過(guò)在hibernate.cfg.xml中執(zhí)行current_session_context_class的屬性來(lái)決定是采用thread或jta或自定義的方式來(lái)產(chǎn)生session。
2.5. CommonDao
在持久層部分目前采用的較多的仍然是dao模式,Hibernate作為ORM工具已經(jīng)提供了CRUD的封裝,類如可以使用session.save();session.persist()這樣簡(jiǎn)單的方式來(lái)完成CRUD的操作,但在實(shí)際的項(xiàng)目中還是需要提供一個(gè)通用的Dao,來(lái)簡(jiǎn)化對(duì)于事務(wù)、異常處理以及session的操作,同時(shí)提供一些項(xiàng)目中需要的相關(guān)操作。
三. 開發(fā)
在完成了Hibernate的開發(fā)環(huán)境的搭建后,就可以基于Hibernate進(jìn)行持久層的開發(fā)了,對(duì)于持久層開發(fā)來(lái)說(shuō),會(huì)涉及到實(shí)體的編寫、實(shí)體的維護(hù)以及實(shí)體的查詢?nèi)齻€(gè)部分。
3.1. 實(shí)體的編寫
Hibernate 的一個(gè)明顯的優(yōu)點(diǎn)就是在于可透明化的對(duì)對(duì)象進(jìn)行持久,這也就意味著持久對(duì)象根本就不需要依賴任何的東西,可以采用POJO的方式來(lái)編寫,在Hibernate 3以上版本還提供了對(duì)于Map、XML的方式的持久的支持,就更方便了,在項(xiàng)目中,更多采用的仍然是POJO的方式。
在實(shí)體的編寫上應(yīng)該說(shuō)不會(huì)有什么問(wèn)題,只要仔細(xì)查看xdoclet關(guān)于hibernatedoclet部分的說(shuō)明即可完成。
這塊需要學(xué)習(xí)的主要是普通的值類型注釋的編寫、id字段注釋的編寫、關(guān)聯(lián)注釋的編寫,這些部分xdoclet均提供了較詳細(xì)的說(shuō)明。
3.2. 實(shí)體的維護(hù)
3.2.1. 新增 / 編輯 / 刪除
新增 / 編輯 / 刪除是持久操作中最常使用的維護(hù)性操作,基于 Hibernate 做這樣的維護(hù)就比采用 sql 的方式簡(jiǎn)單多了,通過(guò)上面 CommonDao ,就可以直接完成 dao.save 、 dao.update 、 dao.delete 的操作,而且在 Hibernate 3 也支持了批量的 insert 、 update 和 delete 。
這個(gè)部分中需要注意的是 Hibernate 對(duì)于對(duì)象的三種狀態(tài)的定義:
u Transient
很容易理解,就是從未與 session 發(fā)生過(guò)關(guān)系的對(duì)象, ^_^ ,例如在代碼中直接 User user=new User() ;這樣形成的 user 對(duì)象,就稱為 Transient 對(duì)象了。
u Detached
同樣很容易理解,就是與 session 發(fā)生過(guò)關(guān)系的對(duì)象,但 session 已經(jīng)關(guān)閉了的情況下存在的對(duì)象,例如:
User user=new User();
user.setName(“bluedavy”);
session.save(user);
session.close();
在 session.close() 后這個(gè)時(shí)候的 user 對(duì)象就處于 Detached 狀態(tài)之中了,如果想將這個(gè)對(duì)象變?yōu)?/span> Persistent 狀態(tài),可以通過(guò) session.merge 或 session.saveOrUpdate() 等方式來(lái)實(shí)現(xiàn)。
Detached 狀態(tài)的對(duì)象在實(shí)際的應(yīng)用中最常采用,從概念上我們可以這么理解,處于 Detached 狀態(tài)的對(duì)象可以看做是一個(gè) DTO ,而不是 PO ,這從很大程度上就方便了 PO 在實(shí)際項(xiàng)目中的使用了。
u Persistent
Persistent 狀態(tài)就是指和 Session 發(fā)生了關(guān)系的對(duì)象,并且此時(shí) session 未關(guān)閉,舉例如下:
User user=new User();
user.setName(“bluedavy”);
session.save(user);
user.getName();
在 session.save 后 user 就處于 Persistent 狀態(tài),此時(shí)如果通過(guò) session 根據(jù) user 的 id 去獲取 user 對(duì)象,則可發(fā)現(xiàn)獲取的對(duì)象和之前的 user 是同一個(gè)對(duì)象,這是 session 一級(jí)緩存所起的作用了,當(dāng)然,也可以強(qiáng)制的刷新 session 的一級(jí)緩存,讓 session 從數(shù)據(jù)庫(kù)中重新獲取,只需要在獲取前執(zhí)行 session.evict(user) 或 session.clear() 。
3.2.2. 關(guān)聯(lián)維護(hù)
關(guān)聯(lián)維護(hù)在 Hibernate 中表現(xiàn)出來(lái)可能會(huì)讓熟悉使用 sql 的人有些的不熟,但其實(shí)以對(duì)象的觀點(diǎn)去看是會(huì)覺得很正常的。
在 Hibernate 的關(guān)聯(lián)維護(hù)中,最重要的是 inverse 和 cascade 兩個(gè)概念。
u inverse
inverse 從詞義上看過(guò)去可能不是那么容易理解,其實(shí)它的意思就是由誰(shuí)來(lái)控制關(guān)聯(lián)關(guān)系的自動(dòng)維護(hù),當(dāng) inverse=true 就意味著當(dāng)前對(duì)象是不能自動(dòng)維護(hù)關(guān)聯(lián)關(guān)系,當(dāng) inverse=false 就意味著當(dāng)前對(duì)象可自動(dòng)維護(hù)關(guān)聯(lián)關(guān)系,還是舉例來(lái)說(shuō):
假設(shè) Org 和 User 一對(duì)多關(guān)聯(lián),
當(dāng) org 中 getUsers 的 inverse=false 的情況:
org.getUsers().add(user);
dao.save(org);
這樣執(zhí)行后將會(huì)看到數(shù)據(jù)庫(kù)中 user 這條記錄中的 orgId 已經(jīng)被設(shè)置上去了。
當(dāng) inverse=true 的情況下,執(zhí)行上面的代碼,會(huì)發(fā)現(xiàn)在數(shù)據(jù)庫(kù)中 user 這條記錄中的 orgId 沒(méi)有被設(shè)置上去。
^_^ , inverse 的作用這樣可能看的不是很明顯,在下面的一對(duì)多中會(huì)加以描述。
u cascade
cascade 的概念和數(shù)據(jù)庫(kù)的 cascade 概念是基本一致的, cascade 的意思形象的來(lái)說(shuō)就是當(dāng)當(dāng)前對(duì)象執(zhí)行某操作的情況下,其關(guān)聯(lián)的對(duì)象也執(zhí)行 cascade 設(shè)置的同樣的操作。
例如當(dāng) org.getUsers 的 cascade 設(shè)置為 delete 時(shí),當(dāng)刪除 org 時(shí),相應(yīng)的 users 也同樣被刪除了,但這個(gè)時(shí)候要注意, org.getUsers 這個(gè)集合是被刪除的 user 的集合,也就是說(shuō)如果這個(gè)時(shí)候數(shù)據(jù)庫(kù)中新增加了一個(gè) user 給 org ,那么這個(gè) user 是不會(huì)被刪除的。
cascade 的屬性值詳細(xì)見《 Hibernate reference 》。
3.2.2.1. 一對(duì)一
一對(duì)一的關(guān)聯(lián)維護(hù)在實(shí)際項(xiàng)目中使用不多,一對(duì)一在Hibernate中可采用兩種方式來(lái)構(gòu)成,一種是主鍵關(guān)聯(lián),一種是外鍵關(guān)聯(lián)。
一對(duì)一的使用推薦使用主鍵關(guān)聯(lián),具體配置方法請(qǐng)參見《Hibernate Reference》。
3.2.2.2. 一對(duì)多/多對(duì)一
一對(duì)多/多對(duì)一的關(guān)聯(lián)維護(hù)在實(shí)際項(xiàng)目中使用是比較多的,在Hibernate中可采用多種方式來(lái)配置一對(duì)多的關(guān)聯(lián),如采用Set、List、Bag、Map等,具體在《Hibernate Reference》中都有詳細(xì)說(shuō)明。
在這里我想說(shuō)的一點(diǎn)就是關(guān)于inverse的設(shè)置,在一對(duì)多的情況下建議將一端的inverse設(shè)為true,而由多端去自動(dòng)維護(hù)關(guān)聯(lián)關(guān)系,為什么這樣做其實(shí)挺容易理解的,假設(shè)org和user為一對(duì)多的關(guān)聯(lián),org.getUsers的inverse設(shè)置為false,org.getUsers().add(user);dao.update(org);當(dāng)update的時(shí)候org所關(guān)聯(lián)的所有user的orgId都會(huì)更新一次,可想而知這個(gè)效率,而如果改為在多端維護(hù)(多端設(shè)置為inverse=false),則是這樣:user.setOrg(org);dao.update(user);當(dāng)update的時(shí)候就僅僅是更新user這一條記錄而已。
另外一點(diǎn)就是合理的設(shè)置cascade,這個(gè)要根據(jù)需求來(lái)實(shí)際決定。
3.2.2.3. 多對(duì)多
多對(duì)多的關(guān)聯(lián)維護(hù)在實(shí)際項(xiàng)目中其實(shí)也是比較多的,盡管在《Hibernate Reference》中認(rèn)為多對(duì)多的情況其實(shí)很多時(shí)候都是設(shè)計(jì)造成的。
多對(duì)多的關(guān)聯(lián)也同樣可以采用Set、List等多種方式來(lái)配置,具體在《Hibernate Reference》中也有詳細(xì)的說(shuō)明。
多對(duì)多的關(guān)聯(lián)維護(hù)上沒(méi)有什么需要多說(shuō)的,在實(shí)踐過(guò)程中來(lái)看這塊不會(huì)出什么太多問(wèn)題,唯一需要注意的是合理設(shè)置cascade,這個(gè)要根據(jù)項(xiàng)目的實(shí)際情況而定。
3.3. 實(shí)體的查詢
Hibernate 提供了多種方式來(lái)支持實(shí)體的查詢,如對(duì)于原有熟悉sql的人可以繼續(xù)使用sql,符合對(duì)象語(yǔ)言的對(duì)象查詢語(yǔ)句(HQL)以及條件查詢API(Criteria)。
在熟練使用hql或criteria的情況下,我相信你會(huì)覺得Hibernate的查詢方式會(huì)比采用sql的方式更加簡(jiǎn)便。
3.3.1. 符合對(duì)象語(yǔ)言的查詢語(yǔ)句
Hibernate 提供了一種符合對(duì)象語(yǔ)言的查詢語(yǔ)句,稱為 HQL ,這種語(yǔ)句的好處是能夠避免使用 sql 的情況下依賴數(shù)據(jù)庫(kù)特征的情況出現(xiàn),同時(shí)它帶來(lái)的最大的好處就是我們能夠根據(jù) OO 的習(xí)慣去進(jìn)行實(shí)體的查詢。
對(duì)于 HQL 沒(méi)有什么多講的,如果熟悉 sql 的人應(yīng)該也是能夠很快就學(xué)會(huì) HQL ,而如果不熟悉 sql 的人那也沒(méi)關(guān)系, HQL 的上手是非常容易的,具體請(qǐng)參考《 Hibernate Reference 》。
3.3.2. 占位符式的查詢
占位符式的查詢 ( 就是采用 ? 替換查詢語(yǔ)句中的變量 ) 是在采用 sql 的情況下經(jīng)常使用的一種查詢方式,也是查詢時(shí)推薦使用的一種方式。
Hibernate 中的查詢參數(shù)主要有兩種類型:值類型和實(shí)體類型,值類型就是指一個(gè)切實(shí)的值 ( 如 String 、 int 、 List 這些 ) ,實(shí)體類型就是一個(gè)具體的實(shí)體,如編寫的 User 、 Organization 等,值類型的查詢和普通 sql 幾乎一樣,而實(shí)體類型的查詢就體現(xiàn)了 Hibernate 的強(qiáng)項(xiàng), ^_^ ,可以起到簡(jiǎn)化 sql 的作用,并且使得查詢語(yǔ)句更加容易理解。
3.3.2.1. 值類型
3.3.2.1.1. 簡(jiǎn)單值
舉例如下:
from User u where u.name=:username and u.yearold=:yearold
這就是一個(gè)常見的簡(jiǎn)單值的占位符式的查詢,通過(guò)這樣的方式就可以把值注入到參數(shù)中:
query.setParameter(“username”,”bluedavy”);
query.setParameter(“yearold”,25);
同樣, hibernate 也支持和 sql 完全相同的 ? 的方式,那么上面的語(yǔ)句以及注入?yún)?shù)的方式就變?yōu)榱耍?/span>
from User u where u.name=? and u.yearold=?
query.setParameter(0,”bluedavy”);
query.setParameter(1,25);
推薦使用第一種,那樣參數(shù)的意義更容易被理解。
3.3.2.1.2. in 查詢
in 查詢也是經(jīng)常被使用到的一種查詢,在 Hibernate 中表現(xiàn)出來(lái)會(huì)稍有不同,不過(guò)如果按照對(duì)象觀點(diǎn)去看就很容易理解了,例如下面這句:
from User u where u.name in (:usernameList)
在 Hibernate 中通過(guò)這樣的方式將值注入到這個(gè)參數(shù)中:
List list=new ArrayList();
list.add(“jerry”);
list.add(“bluedavy”);
query.setParameterList(“usernameList”,list);
在 sql 中通常是組裝一個(gè)由 , 連接的值來(lái)構(gòu)成 in 中的參數(shù)值,而在 Hibernate 中則依照對(duì)象轉(zhuǎn)化為采用 list 了, ^_^ ,是不是更方便些。
3.3.2.2. 實(shí)體類型
在Hibernate中關(guān)聯(lián)采用的都是對(duì)象形式,表現(xiàn)對(duì)外就是隱藏了數(shù)據(jù)庫(kù)的外鍵的部分,這也就對(duì)習(xí)慣使用sql查詢的人帶來(lái)一個(gè)問(wèn)題,因?yàn)闊o(wú)法再操作外鍵字段,那么在涉及到關(guān)聯(lián)的實(shí)體的查詢時(shí)應(yīng)該怎么做呢,我把它分為單實(shí)體和實(shí)體集合兩種情況來(lái)說(shuō)說(shuō)。
3.3.2.2.1. 單實(shí)體
單實(shí)體的查詢對(duì)應(yīng)到 sql 情況通常是在一對(duì)多的情況下通過(guò)多端查詢同時(shí)結(jié)合一端的一些過(guò)濾條件,在 sql 中通常采用 join 的方式來(lái)實(shí)現(xiàn)這個(gè),而在 Hibernate 中要實(shí)現(xiàn)這點(diǎn)就更容易了,舉例如下:
User 和 Organization 是一對(duì)多,現(xiàn)在要查詢屬于組織機(jī)構(gòu)名稱為 ”Blogjava” 以及用戶年齡大于 20 的用戶:
from User u where u.org.name=:orgname and u.yearold>:yearold
query.setParameter(“orgname”,”Blogjava”);
query.setParameter(“yearold”,20);
可以看到這樣的查詢語(yǔ)句比 sql 更簡(jiǎn)單多了,同時(shí)也更容易理解多了。
3.3.2.2.2. 實(shí)體集合
實(shí)體集合過(guò)濾形式的查詢?cè)趯?shí)際的項(xiàng)目中也經(jīng)常會(huì)碰到,仍然用上面的例子,但改為通過(guò) Organization 去查詢:
from Organization org where org.name=:orgname and org.users.yearold>:yearold
是不是比 sql 簡(jiǎn)單多了,而且更容易理解呢, ^_^
這個(gè)時(shí)候?qū)ο蠡樵冋Z(yǔ)句的優(yōu)勢(shì)就體現(xiàn)出來(lái)了,而不用陷入 sql 的那種關(guān)系型的通過(guò)外鍵進(jìn)行查詢的方式。
3.3.3. NamedQuery
NamedQuery 的意思就是指在 PO 的映射文件中定義關(guān)于 PO 的查詢語(yǔ)句,而在應(yīng)用中指需要直接調(diào)用此查詢語(yǔ)句的別名即可,這個(gè)好處非常明顯,使得所有的查詢語(yǔ)句可以統(tǒng)一的進(jìn)行管理,同樣,我們可以在 PO 中通過(guò) javadoc 的方式進(jìn)行定義,這就更方便了, ^_^
操作 NamedQuery 的方法和普通 hql 的方法基本一樣:
session.getNamedQuery(queryname);
其中的 queryname 就是我們定義的查詢語(yǔ)句的別名,一個(gè) namedQuery 的語(yǔ)句的示例如下:
< query name = "validate" ><![CDATA[
from User u where u.loginname=:loginname and u.password=:password
]]></ query >
3.3.4. Criteria
條件查詢的 API 使得我們可以采用完全對(duì)象化的方式進(jìn)行實(shí)體的查詢,而不是通過(guò) hql 的方式,在實(shí)際項(xiàng)目中,使用 hql 的方式更為居多,畢竟寫起來(lái)更方便。
關(guān)于 Criteria 的具體介紹請(qǐng)參閱《 Hibernate Reference 》。
3.3.5. 原生 SQL
原生 SQL 不推薦使用,但在某些確實(shí)需要用 sql 的情況下那么 Hibernate 還是支持的,具體見《 Hibernate Reference 》。
四. 設(shè)計(jì)
獨(dú)立的編寫這個(gè)章節(jié)的原因是希望在采用Hibernate的情況下充分的去發(fā)揮Hibernate的優(yōu)勢(shì),改變我們以關(guān)系形式去做持久層的設(shè)計(jì)的慣性思維,形成以OO的思想去設(shè)計(jì)持久層,所以我非常推薦通過(guò)寫PO去生成數(shù)據(jù)表的方式,而不是設(shè)計(jì)表反向?qū)С?span>PO的形式,當(dāng)然,對(duì)于原有的系統(tǒng)那就沒(méi)辦法了。
OO 思想中的核心三要素:封裝、繼承和多態(tài),在Hibernate的支持下同樣可以充分發(fā)揮OO的三要素來(lái)優(yōu)化持久層的設(shè)計(jì)。
4.1. 封裝
4.1.1. Component
Hibernate 中有一個(gè) Component 的概念,這就允許在進(jìn)行持久層設(shè)計(jì)的時(shí)候采用細(xì)粒度級(jí)的領(lǐng)域模型進(jìn)行設(shè)計(jì),例如在 User 對(duì)象中需要記錄 User 的 firstname 、 lastname 這些信息,而在其他的表中也有這種需求,那么在 Hibernate 中我們就可以把 firstname 、 lastname 組裝為一個(gè) UserName 對(duì)象,作為 Component 放入 User 中,在 user 中就可以變?yōu)椴捎?/span> user.getUserName.getFristName 的方式來(lái)獲取。
Component 對(duì)于我們采用對(duì)象的封裝概念進(jìn)行持久層設(shè)計(jì)提供了很好的支持,同時(shí)在 Hibernate 中還有 Elements 、 Properties 這些元素,具體請(qǐng)參見《 Hibernate Reference 》。
4.2. 繼承
繼承使得我們可以對(duì)持久層中的對(duì)象進(jìn)行抽象,類如我們可以形成Person這個(gè)對(duì)象,而User、Employee都繼承自這個(gè)對(duì)象。
繼承在數(shù)據(jù)庫(kù)形式的設(shè)計(jì)中固然也可以實(shí)現(xiàn),但通常不能以對(duì)象的觀點(diǎn)去發(fā)揮的淋漓盡致,當(dāng)然不是說(shuō)以對(duì)象的方式去設(shè)計(jì)一定是最好的。
在Hibernate中對(duì)于繼承映射到數(shù)據(jù)表有幾種不同的策略,各有適用的不同場(chǎng)合,具體的解釋和說(shuō)明見《Hibernate Reference》
4.2.1. 單表策略
單表策略很容易理解,就是將類、子類中所有的屬性都放至一張表中,這對(duì)于子類屬性不多的情況非常有效。
在 Hibernate 中通常將子類定義為 @hibernate.subclass 的方式來(lái)實(shí)現(xiàn)這個(gè)策略。
4.2.2. 每個(gè)子類一張表
每個(gè)子類一張表在 Hibernate 中有幾種實(shí)現(xiàn)方式, @hibernate.join-subclass 、 @hibernate.join-subclass-key 的組合方式以及 @hibernate.join-subclass 、 @hibernate.discriminator 的組合方式是較常用的兩種方式,第一種方式采用的是主鍵關(guān)聯(lián)方式,第二種方式采用的是 discriminator 字段的關(guān)聯(lián)方式,個(gè)人比較推崇第一種方式。
這種策略適合在子類屬性和父類有較大不同的情況下采用。
4.2.3. 每個(gè)具體類一張表
這種策略適合在類層次結(jié)構(gòu)上有一定數(shù)量的抽象類的情況下使用,同樣有兩種方式,一種是采用顯式多態(tài)的方式,另一種是采用隱式多態(tài)的方式,顯式多態(tài)采用的為 @hibernate.union-subclass 的方式,隱式多態(tài)則采用每個(gè)具體類的 PO 獨(dú)立建表的策略,在它的映射文件中將看不出任何的和接口、抽象類的關(guān)系,同時(shí)對(duì)于抽象類,需要指明其 abstract=”true” 。
4.3. 多態(tài)
4.3.1. 查詢
在查詢中很容易體現(xiàn) Hibernate 對(duì)于多態(tài)的支持,如系統(tǒng)有 Person 對(duì)象、 User 和 Employee 分別繼承自 Person ,同時(shí) Person 和 Organization 對(duì)象關(guān)聯(lián),這個(gè)時(shí)候我們通過(guò) Organization 獲取其關(guān)聯(lián)的 Person 時(shí)得到的既有可能是 User ,也有可能是 Employee , ^_^…
五. 性能
Hibernate 作為ORM工具,從性能上來(lái)講帶給了很多人憂慮,但我覺得Hibernate在性能上也許會(huì)帶來(lái)少許的降低,但如果對(duì)于不能合理設(shè)計(jì)數(shù)據(jù)庫(kù)和使用SQL的人來(lái)說(shuō),我覺得Hibernate反倒能提高性能,除非是在一些特殊的場(chǎng)合,如報(bào)表式的那種查詢推薦繼續(xù)采用JDBC的方式。
Hibernate 在性能提升上其實(shí)有很多種做法,在《Hibernate Reference》中也有專門的提升性能的章節(jié),在這里我提幾點(diǎn)在項(xiàng)目中通常采用的方法。
5.1. Lazy Load
Lazy Load 是常用的一種提升性能的方法,這個(gè)其實(shí)很容易理解,在不采用lazy load的情況下,Hibernate在獲取一個(gè)PO的時(shí)候,將同時(shí)獲取PO中的屬性、PO中的集合以及集合中對(duì)象的屬性、集合,這樣看過(guò)去很容易看出,如果對(duì)象的關(guān)聯(lián)結(jié)構(gòu)有深層次的話,最后搞不好整個(gè)庫(kù)都被加載出來(lái)了,而在實(shí)際使用中往往可能只需要用到PO中的一兩個(gè)屬性而已,這點(diǎn)也是之前的ORM產(chǎn)品經(jīng)常被批的一點(diǎn),就是ORM產(chǎn)品不能象sql那樣只獲取需要的東西,^_^,其實(shí)Hibernate在這點(diǎn)上一直就支持,而且支持的還不錯(cuò),在Hibernate 3以后,默認(rèn)的lazy就已經(jīng)設(shè)置為true了,這個(gè)時(shí)候包括po中的屬性都是采用lazy load的方式,只有在調(diào)用到這個(gè)屬性時(shí)才會(huì)從緩存或數(shù)據(jù)庫(kù)中加載,當(dāng)然,集合也同樣如此。
在lazy load上推薦不要什么字段都采用lazy load的方式,對(duì)于一些基本屬性的字段建議將其lazy設(shè)置為false,而對(duì)于一些可能需要消耗內(nèi)存的字段,如clob這樣的字段對(duì)象的lazy設(shè)置為true,對(duì)于集合則全部設(shè)置為lazy=true。
是否采用Lazy load對(duì)系統(tǒng)的性能會(huì)有非常明顯的影響,同時(shí)盡量不要將Detached Object放入Http的session中。
5.1.1. OSIV
OSIV : Open Session In View ,在 B/S 系統(tǒng)中通常采用這種方式來(lái)更好的去支持 Lazy load ,意思就是在 View 加載前打開 Session ,在 View 加載完畢后關(guān)閉 Session 的方式,在 Spring 中有 OpenSessionInViewFilter ,可參考或直接使用。
5.2. Cache
Cache 是在提升系統(tǒng)性能方面常用的方法,在Hibernate中通常有非常好的對(duì)于Cache的支持方法,Hibernate中對(duì)于Cache有一級(jí)緩存和二級(jí)緩存的概念,一級(jí)緩存是必須的,位于Session部分,二級(jí)緩存則不是必須的,由開發(fā)人員自行指定,二級(jí)緩存可指定使用何種開源的cache工具,Hibernate 3以后的版本默認(rèn)使用的是Ehcache,也可以切換為Oscache、JbossCache,對(duì)我而言最重要的區(qū)別在于對(duì)于cluster的支持上。
二級(jí)緩存能夠明顯的提高系統(tǒng)的性能,當(dāng)然,同時(shí)也會(huì)更加的消耗內(nèi)存,可以通過(guò)配置文件來(lái)指定內(nèi)存中能夠加載的最多的元素,這有利于避免消耗過(guò)多內(nèi)存。
二級(jí)緩存的設(shè)置在Hibernate中非常的簡(jiǎn)單,只需要在相應(yīng)的hbm.xml中增加cache元素,指明使用何種策略,如read-only、read-write等,也可以直接在hibernate.cfg.xml中增加class-cache的方式來(lái)進(jìn)行全局指定。
5.3. 高效的查詢語(yǔ)句
查詢語(yǔ)句的是否高效對(duì)于系統(tǒng)的性能也是會(huì)造成明顯的影響的,為了方便系統(tǒng)性能的調(diào)優(yōu),建議大家對(duì)查詢語(yǔ)句進(jìn)行統(tǒng)一管理,如統(tǒng)一采用NamedQuery的方式,在這樣的情況下可以在系統(tǒng)運(yùn)行時(shí)請(qǐng)教數(shù)據(jù)庫(kù)專家,由他們來(lái)分析系統(tǒng)中的查詢語(yǔ)句的執(zhí)行效率以及提出改進(jìn)策略,而對(duì)于開發(fā)人員來(lái)講,在查詢語(yǔ)句方面最能夠注意的就是采用占位符式的查詢。
5.3.1. 占位符式的查詢
數(shù)據(jù)庫(kù)對(duì)于所有的 sql 語(yǔ)句都要進(jìn)行語(yǔ)法分析,而其分析通常會(huì)受到語(yǔ)句中的大小寫、空格以及參數(shù)不同的影響,在其語(yǔ)法分析器認(rèn)為不同的情況下將再次進(jìn)行分析,這就不可避免的降低了響應(yīng)的速度,而采用占位符式的查詢則可保證語(yǔ)法分析器只進(jìn)行一次的分析,在參數(shù)不同的情況并不會(huì)出現(xiàn)重復(fù)解析的現(xiàn)象,其次就是要統(tǒng)一查詢語(yǔ)句的編寫風(fēng)格,包括大小寫、空格這些。
我不是很確定 Hibernate 中對(duì)于語(yǔ)句的語(yǔ)法分析,估計(jì)和數(shù)據(jù)庫(kù)的這種方式應(yīng)該差不多,不過(guò)猜想可能會(huì)更智能一些, ^_^
5.4. 一些配置
在 hibernate.cfg.xml 中的一些配置也會(huì)對(duì)性能產(chǎn)生一定的影響,如 jdbc.fetch_size 的設(shè)置等,還有象采用連接池方面的設(shè)置,對(duì)于 B/S 應(yīng)用的情況建議盡量采用應(yīng)用服務(wù)器提供的 JNDI 的方式。
5.5. 建議
在性能提升方面從兩方面入手,一是持久層對(duì)象的設(shè)計(jì)上,這方面可以參考《 Hibernate Reference 》中提升性能章節(jié)中的一些建議,另一方面則是請(qǐng)教數(shù)據(jù)庫(kù)專家,由數(shù)據(jù)庫(kù)專家對(duì)表結(jié)構(gòu)、查詢語(yǔ)句等進(jìn)行分析來(lái)給出改進(jìn)策略,在現(xiàn)有的一個(gè)項(xiàng)目中,竟然有出現(xiàn) Hibernate 在外鍵上沒(méi)建立索引的現(xiàn)象出現(xiàn)?
六. 測(cè)試
6.1. 編寫專門的測(cè)試用的配置文件
測(cè)試方面也是極度關(guān)心的話題,在測(cè)試方面其實(shí)比較簡(jiǎn)單,只需要在測(cè)試類中采用專門用于測(cè)試的配置文件即可,在這個(gè)配置文件中,通過(guò)都是采用設(shè)置hbm2ddl.auto屬性為create-drop的方式,也就是在測(cè)試類運(yùn)行前創(chuàng)建表,在測(cè)試類運(yùn)行后刪除表的策略,在更多的情況下,我們可以采用in-memory的數(shù)據(jù)庫(kù)的方式,如hsql,當(dāng)然,有些時(shí)候則需要和實(shí)際運(yùn)行環(huán)境一致,那么就需要采用建立專門的測(cè)試庫(kù)的方式,避免測(cè)試數(shù)據(jù)和運(yùn)行數(shù)據(jù)的相互影響。
七. 企業(yè)應(yīng)用開發(fā)
事務(wù)和并發(fā)是企業(yè)應(yīng)用開發(fā)中非常關(guān)注的兩個(gè)話題,在《Hibernate Reference》中提供了詳細(xì)的方案,在這里我就簡(jiǎn)單的說(shuō)說(shuō)。
7.1. 事務(wù)
事務(wù)是企業(yè)應(yīng)用開發(fā)中非常重視的一點(diǎn),而在Hibernate中操作此部分和sql方式?jīng)]有什么很大的區(qū)別,可以通過(guò)session主動(dòng)去獲取Transaction來(lái)實(shí)現(xiàn)事務(wù)控制,同時(shí)也可以交由應(yīng)用服務(wù)器提供的JTA來(lái)實(shí)現(xiàn)事務(wù)控制。
在事務(wù)這個(gè)級(jí)別上如果有更高的要求,建議采用Spring的事務(wù)框架。
7.2. 并發(fā)
在并發(fā)方面多采用鎖策略,鎖策略和數(shù)據(jù)庫(kù)基本相同,同樣是樂(lè)觀鎖和悲觀鎖兩種策略,樂(lè)觀鎖策略在Hibernate中推薦使用version或timestamp來(lái)實(shí)現(xiàn),具體覆蓋方式則需要根據(jù)應(yīng)用而定,如是采用最新的修改的覆蓋還是采用版本沖突策略等,悲觀鎖策略則通過(guò)指定對(duì)象的鎖方式,如LockMode.READ,引用《Hibernate Reference》中的一段話:
“用戶其實(shí)并不需要花很多精力去擔(dān)心鎖定策略的問(wèn)題。通常情況下,只要為JDBC連接指定一下隔離級(jí)別,然后讓數(shù)據(jù)庫(kù)去搞定一切就夠了。然而,高級(jí)用戶有時(shí)候希望進(jìn)行一個(gè)排它的悲觀鎖定,或者在一個(gè)新的事務(wù)啟動(dòng)的時(shí)候,重新進(jìn)行鎖定。Hibernate總是使用數(shù)據(jù)庫(kù)的鎖定機(jī)制,從不在內(nèi)存中鎖定對(duì)象!
如果數(shù)據(jù)庫(kù)不支持用戶設(shè)置的鎖定模式,Hibernate將使用適當(dāng)?shù)奶娲J剑@一點(diǎn)可以確保應(yīng)用程序的可移植性。”。
用戶可通過(guò)幾種方式來(lái)指定鎖定模式:
u Session.load() 的時(shí)候指定鎖定模式LockMode;
u Session.lock() ;
u Query.setLockMode() 。
八. 相關(guān)書籍
Hibernate 上手并不難,但要真正的用好它確實(shí)不是件容易的事,有些書籍能夠很好的幫我們快速的提供解決思路和解決方案,而這些書籍我們也應(yīng)該常備,以方便自己在有些問(wèn)題上的解答。
同時(shí),我一直堅(jiān)持的觀點(diǎn),一種開源框架通常帶來(lái)的不僅僅是開發(fā)、使用上的改變,帶來(lái)的最大的改變?nèi)匀皇窃谠O(shè)計(jì)層次上的,設(shè)計(jì)上能否充分的發(fā)揮開源框架的優(yōu)勢(shì)才是最為重要的。
8.1. 《Hibernate Reference》
這本沒(méi)什么說(shuō)的,必讀書籍,也許在讀的時(shí)候很多東西你不會(huì)覺得什么,但當(dāng)碰到一些確定方向的問(wèn)題時(shí),可以通過(guò)此書快速的查找到相應(yīng)的解決方案,感謝Redsaga組織的翻譯工作,使得我們可以有中文版可看。
目前版本(Hibernate 3.1.2)的下載地址:
http://www.redsaga.com/hibernate-ref/3.1.2/zh-cn/pdf/hibernate_reference.pdf
8.2. 《Hibernate in action》
In action 系列的書籍也沒(méi)啥多說(shuō)的,強(qiáng)烈推薦看看,盡管現(xiàn)在看起來(lái)版本有些老了,但里面很多的實(shí)踐思想仍然是非常值得學(xué)習(xí)的,網(wǎng)上應(yīng)該有很多電子版下載的地方。
8.3. 《深入淺出Hibernate》
這本書想必大家也聽聞了不少,簡(jiǎn)稱白皮書,^_^,是夏昕、曹曉剛以及唐勇三位大師的大作。
posted on 2007-08-15 21:14
冬天出走的豬 閱讀(366)
評(píng)論(0) 編輯 收藏 所屬分類:
hibernate