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