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