標籤: 暫無標籤

Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲的使用對象編程思維來操縱資料庫。Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。

1 hibernate -基本簡介

hibernatehibernate
hibernatehibernate
大多數應用程序都需要處理數據。Java應用程序運行時,往往把數據封裝為相互連接的對象網路,但是當程序結束時,這些對象就會消失在一團邏輯中,所以需要有一些保存它們的方法。有時候,甚至在編寫應用程序之前,數據就已經存在了,所以需要有讀入它們和將其表示為對象的方法。手動編寫代碼來執行這些任務不僅單調乏味、易於出錯,而且會佔用整個應用程序的很大一部分開發工作量。

優秀的面向對象開發人員厭倦了這種重複性的勞動,他們開始採用通常的「積極」偷懶做法,即創建工具,使整個過程自動化。對於關係資料庫來說,這種努力的最大成果就是對象/關係映射(ORM)工具。

這類工具有很多,從昂貴的商業產品到內置於J2EE中的EJB標準。然而,在很多情況下,這些工具具有自身的複雜性,使得開發人員必須學習使用它們的詳細規則,並修改組成應用程序的類以滿足映射系統的需要。由於這些工具為應付更加嚴格和複雜的企業需求而不斷發展,於是在比較簡單和常見的場景中,使用它們所面臨的複雜性反而蓋過了所能獲得的好處。這引起了一場革命,促進了輕量級解決方案的出現,而Hibernate就是這樣的一個例子。

2 hibernate -核心介面

hibernatehibernate
Hibernate的核心介面一共有5個,分別為:Session、SessionFactory、Transaction、Query和Configuration。這5個核心介面在任何開發中都會用到。通過這些介面,不僅可以對持久化對象進行存取,還能夠進行事務控制。下面對這五的核心介面分別加以介紹。

Session介面:Session介面負責執行被持久化對象的CRUD操作(CRUD的任務是完成與資料庫的交流,包含了很多常見的SQL語句。)。但需要注意的是Session對象是非線程安全的。同時,Hibernate的session不同於JSP應用中的HttpSession。這裡當使用session這個術語時,其實指的是Hibernate中的session,而以後會將HttpSesion對象稱為用戶session。

SessionFactory介面:SessionFactroy介面負責初始化Hibernate。它充當數據存儲源的代理,並負責創建Session對象。這裡用到了工廠模式。需要注意的是SessionFactory並不是輕量級的,因為一般情況下,一個項目通常只需要一個SessionFactory就夠,當需要操作多個資料庫時,可以為每個資料庫指定一個SessionFactory。

Configuration介面:Configuration介面負責配置並啟動Hibernate,創建SessionFactory對象。在Hibernate的啟動的過程中,Configuration類的實例首先定位映射文檔位置、讀取配置,然後創建SessionFactory對象。

Transaction介面:Transaction介面負責事務相關的操作。它是可選的,開發人員也可以設計編寫自己的底層事務處理代碼。

Query和Criteria介面:Query和Criteria介面負責執行各種資料庫查詢。它可以使用HQL語言或SQL語句兩種表達方式。

3 hibernate -主鍵介紹

hibernatehibernate
Assigned:Assigned方式由程序生成主鍵值,並且要在save()之前指定否則會拋出異常。

特點:主鍵的生成值完全由用戶決定,與底層資料庫無關。用戶需要維護主鍵值,在調用session.save()之前要指定主鍵值。

Hilo:Hilo使用高低位演算法生成主鍵,高低位演算法使用一個高位值和一個低位值,然後把演算法得到的兩個值拼接起來作為資料庫中的唯一主鍵。Hilo方式需要額外的資料庫表和欄位提供高位值來源。默認請況下使用的表是hibernate_unique_key,默認欄位叫作next_hi。next_hi必須有一條記錄否則會出現錯誤。

特點:需要額外的資料庫表的支持,能保證同一個資料庫中主鍵的唯一性,但不能保證多個資料庫之間主鍵的唯一性。Hilo主鍵生成方式由Hibernate維護,所以Hilo方式與底層資料庫無關,但不應該手動修改hi/lo演算法使用的表的值,否則會引起主鍵重複的異常。

Increment:Increment方式對主鍵值採取自動增長的方式生成新的主鍵值,但要求底層資料庫的支持Sequence。如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment標誌符的設置。

特點:由Hibernate本身維護,適用於所有的資料庫,不適合多進程併發更新資料庫,適合單一進程訪問資料庫。不能用於群集環境。

hibernatehibernate
Identity:Identity當時根據底層資料庫,來支持自動增長,不同的資料庫用不同的主鍵增長方式。

特點:與底層資料庫有關,要求資料庫支持Identity,如MySQl中是auto_increment,SQLServer中是Identity,支持的資料庫有MySql、SQLServer、DB2、Sybase和HypersonicSQL。Identity無需Hibernate和用戶的干涉,使用較為方便,但不便於在不同的資料庫之間移植程序。

Sequence:Sequence需要底層資料庫支持Sequence方式,例如Oracle資料庫等。

特點:需要底層資料庫的支持序列,支持序列的資料庫有DB2、PostgreSql、Qracle、SAPDb等在不同資料庫之間移植程序,特別從支持序列的資料庫移植到不支持序列的資料庫需要修改配置文件。

Native:Native主鍵生成方式會根據不同的底層資料庫自動選擇Identity、Sequence、Hilo主鍵生成方式。

特點:根據不同的底層資料庫採用不同的主鍵生成方式。由於Hibernate會根據底層資料庫採用不同的映射方式,因此便於程序移植,項目中如果用到多個資料庫時,可以使用這種方式。

UUID:UUID使用128位UUID演算法生成主鍵,能夠保證網路環境下的主鍵唯一性,也就能夠保證在不同資料庫及不同伺服器下主鍵的唯一性。

特點:能夠保證資料庫中的主鍵唯一性,生成的主鍵佔用比較多的存貯空間。

ForeignGUID:Foreign用於一對一關係中。GUID主鍵生成方式使用了一種特殊演算法,保證生成主鍵的唯一性,支持SQLServer和MySQL。

4 hibernate -源碼對照

hibernatehibernate
net.sf.hibernate. 
該包的類基本上都是介面類和異常類
net.sf.hibernate.cache. 
JCS的實現類
net.sf.hibernate.cfg. 
配置文件讀取類
net.sf.hibernate.collection. 
Hibernate集合介面實現類,例如List,Set,Bag等等,Hibernate之所以要自行編寫集合介面實現類是為了支持lazyloading
net.sf.hibernate.connection. 
幾個資料庫連接池的Provider
net.sf.hibernate.dialect. 
支持多種資料庫特性,每個Dialect實現類代表一種資料庫,描述了該資料庫支持的數據類型和其它特點,例如是否有AutoIncrement,是否有Sequence,是否有分頁sql等等
net.sf.hibernate.eg. 
Hibernate文檔中用到的例子
net.sf.hibernate.engine. 
這個包的類作用比較散
net.sf.hibernate.expression. 
HQL支持的表達式
net.sf.hibernate.hq. 
HQL實現
net.sf.hibernate.id. 
ID生成器
net.sf.hibernate.impl. 
hibernatehibernate
最核心的包,一些重要介面的實現類,如果Session,SessionFactory,Query等
net.sf.hibernate.jca. 
JCA支持,把Session包裝為支持JCA的介面實現類
net.sf.hibernate.jmx. 
我不懂JMX,只知道JMX是用來編寫AppServer的管理程序的,大概是JMX部分介面的實現,使得AppServer可以通過JMX介面管理Hibernate
net.sf.hibernate.loader. 
也是很核心的包,主要是生成sql語句的
net.sf.hibernate.lob. 
Blob和Clob支持
net.sf.hibernate.mapping. 
hbm文件的屬性實現
net.sf.hibernate.metadata. 
PO的Meta實現
net.sf.hibernate.odmg. 
ODMG是一個ORM標準,這個包是ODMG標準的實現類
net.sf.hibernate.persister. 
核心包,實現持久對象和表之間的映射
net.sf.hibernate.proxy. 
hibernatehibernate
Proxy和LazyLoading支持
net.sf.hibernate.ps. 
該包是PreparedStatmentCache
net.sf.hibernate.sql. 
生成JDBCsql語句的包
net.sf.hibernate.test. 
測試類,你可以用junit來測試Hibernate
net.sf.hibernate.tool.hbm2ddl. 
用hbm配置文件生成DDL
net.sf.hibernate.transaction. 
HibernateTransaction實現類
net.sf.hibernate.type. 
Hibernate中定義的持久對象的屬性的數據類型
net.sf.hibernate.util. 
一些工具類,作用比較散
net.sf.hibernate.xml. 
XML數據綁定。

5 hibernate -類庫簡介

hibernatehibernate
antlr(必需):Hibernate使用ANTLR來產生查詢分析器,這個類庫在運行環境下時也是必需的。

Dom4j(必需):Hibernate使用dom4j解析XML配置文件和XML映射元文件。

cglib,asm(必需):Hibernate在運行時使用這個代碼生成庫增強類(與JAVA反射機制聯合使用)。

CommonsCollections,CommonsLogging(必需):Hibernat使用ApacheJakartaCommons項目提供的多個工具類庫。

ehcache(必需):Hibernate可以使用不同cache緩存工具作為二級緩存。EHCache是預設的cache緩存工具。

Log4j(可選):Hibernate使用CommonsLoggingAPI,它也可以依次使用Log4j作為底層實施log的機制。如果上下文類目錄中存在Log4j庫,則CommonsLogging使用Log4j和並它在上下文類路徑中尋找的log4j.properties文件。你可以使用在Hibernate發行包中包含中的那個示例Log4j的配置文件。這樣,把log4j.jar和它的配置文件(位於src/目錄中)拷貝到你的上下文類路徑下,就可以在後台看到底程序如何運行的。

其他文件是不是必需的:請察看Hibernate發行包中的lib/README.txt文件,這是一個Hibernate發行包中附帶的第三方類庫的列表,他們總是保持最新的。你可以在那裡找到所有必需或者可選的類庫(注意:其中的"buildtimerequired"指的是編譯Hibernate時所需要而非編譯你自己的程序所必需的類庫)。

6 hibernate -緩存管理

Hibernate 中提供了兩級Cache,第一級別的緩存是Session級別的緩存,它是屬於事務範圍的緩存。這一級別的緩存由hibernate管理的,一般情況下無 需進行干預;第二級別的緩存是SessionFactory級別的緩存,它是屬於進程範圍或群集範圍的緩存。這一級別的緩存可以進行配置和更改,並且可以 動態載入和卸載。 Hibernate還為查詢結果提供了一個查詢緩存,它依賴於第二級緩存。

1. 一級緩存和二級緩存的比較:

第一級緩存 第二級緩存 存放數據的形式

相互關聯的持久化對象 對象的散裝數據 緩存的範圍 事務範圍,每個事務都有單獨的第一級緩存 進程範圍或集群範圍,緩存被同一個進程或集群範圍內的所有事務共享 併發訪問策略 由於每個事務都擁有單獨的第一級緩存,不會出現併發問題,無需提供併發訪問策略 由於多個事務會同時訪問第二級緩存中相同數據,因此必須提供適當的併發訪問策略,來保證特定的事務隔離級別 數據過期策略 沒有提供數據過期策略。

處於一級緩存中的對象永遠不會過期,除非應用程序顯式清空緩存或者清除特定的對象 必須提供數據過期策略,如基於內存的緩存中的對象的最大數目,允許對象處於緩存中的最長時間,以及允許對象處於緩存中的最長空閑時間 物理存儲介質 內存 內存和硬碟。對象的散裝數據首先存放在基於內在的緩存中,當內存中對象的數目達到數據過期策略中指定上限時,就會把其餘的對象寫入基於硬碟的緩存中。

緩存的軟體實現 在Hibernate的Session的實現中包含了緩存的實現 由第三方提供,Hibernate僅提供了緩存適配器(CacheProvider)。用於把特定的緩存插件集成到Hibernate中。 啟用緩存的方式 只要應用程序通過Session介面來執行保存、更新、刪除、載入和查詢資料庫數據的操作,Hibernate就會啟用第一級緩存,把資料庫中的數據以對象的形式拷貝到緩存中,對於批量更新和批量刪除操作,如果不希望啟用第一級緩存,可以繞過Hibernate API,直接通過JDBC API來執行指操作。 用戶可以在單個類或類的單個集合的粒度上配置第二級緩存。如果類的實例被經常讀但很少被修改,就可以考慮使用第二級緩存。

只有為某個類或集合配置了第二級緩存,Hibernate在運行時才會把它的實例加入到第二級緩存中。 用戶管理緩存的方式 第一級緩存的物理介質為內存,由於內存容量有限,必須通過恰當的檢索策略和檢索方式來限制載入對象的數目。Session的evit()方法可以顯式清空緩存中特定對象,但這種方法不值得推薦。

第 二級緩存的物理介質可以是內存和硬碟,因此第二級緩存可以存放大量的數據,數據過期策略的maxElementsInMemory屬性值可以控制內存中的 對象數目。管理第二級緩存主要包括兩個方面:選擇需要使用第二級緩存的持久類,設置合適的併發訪問策略:選擇緩存適配器,設置合適的數據過期策略。

2. 一級緩存的管理: 當 應用程序調用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調用查詢介面的 list()、iterate()或filter()方法時,如果在Session緩存中還不存在相應的對象,Hibernate就會把該對象加入到第一 級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新資料庫。 Session為應用程序提供了兩個管理緩存的方法: evict(Object obj):從緩存中清除參數指定的持久化對象。 clear():清空緩存中所有持久化對象。

3. 二級緩存的管理:

3.1. Hibernate的二級緩存策略的一般過程如下:

1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有欄位)這樣的SQL語句查詢資料庫,一次獲得所有的數據對象。

2) 把獲得的所有數據對象根據ID放入到第二級緩存中。

3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那麼從二級緩存中查;查不到,再查詢資料庫,把結果按照ID放入到緩存。

4) 刪除、更新、增加數據的時候,同時更新緩存。   

Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query Cache。

3.2. 什麼樣的數據適合存放到第二級緩存中? 1 很少被修改的數據 2 不是很重要的數據,允許出現偶爾併發的數據 3 不會被併發訪問的數據 4 參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其他類的實例引用,實例極少或者從來不會被修改。

3.3. 不適合存放到第二級緩存的數據? 1 經常被修改的數據 2 財務數據,絕對不允許出現併發 3 與其他應用共享的數據。

3.4. 常用的緩存插件 Hibernater 的二級緩存是一個插件,下面是幾種常用的緩存插件:

l EhCache:可作為進程範圍的緩存,存放數據的物理介質可以是內存或硬碟,對Hibernate的查詢緩存提供了支持。

l OSCache:可作為進程範圍的緩存,存放數據的物理介質可以是內存或硬碟,提供了豐富的緩存數據過期策略,對Hibernate的查詢緩存提供了支持。

l SwarmCache:可作為群集範圍內的緩存,但不支持Hibernate的查詢緩存。

l JBossCache:可作為群集範圍內的緩存,支持事務型併發訪問策略,對Hibernate的查詢緩存提供了支持。

3.5. 配置二級緩存的主要步驟:

1) 選擇需要使用二級緩存的持久化類,設置它的命名緩存的併發訪問策略。這是最值得認真考慮的步驟。

2) 選擇合適的緩存插件,然後編輯該插件的配置文件。

7 hibernate -程序性能優化

對於HIBERNATE性能調優的主要考慮點如下:

資料庫設計調整 

 HQL優化 

 API的正確使用(如根據不同的業務類型選用不同的集合及查詢API)

主配置參數(日誌,查詢緩存,fetch_size, batch_size等) 

 映射文件優化(ID生成策略,二級緩存,延遲載入,關聯優化)

一級緩存的管理 

 針對二級緩存,還有許多特有的策略

事務控制策略。

1、 資料庫設計

a) 降低關聯的複雜性

b) 盡量不使用聯合主鍵

c) ID的生成機制,不同的資料庫所提供的機制並不完全一樣

d) 適當的冗餘數據,不過分追求高範式

2、 HQL優化

HQL如果拋開它同HIBERNATE本身一些緩存機制的關聯,HQL的優化技巧同普通的SQL優化技巧一樣,可以很容易在網上找到一些經驗之談。

3、 主配置

a) 查詢緩存,同下面講的緩存不太一樣,它是針對HQL語句的緩存,即完全一樣的語句再次執行時可以利用緩存數據。但是,查詢緩存在一個交易系統(數據變更頻繁,查詢條件相同的機率並不大)中可能會起反作用:它會白白耗費大量的系統資源但卻難以派上用場。

b) fetch_size,同JDBC的相關參數作用類似,參數並不是越大越好,而應根據業務特徵去設置

c) batch_size同上。

d) 生產系統中,切記要關掉SQL語句列印。

4、 緩存

a) 資料庫級緩存:這級緩存是最高效和安全的,但不同的資料庫可管理的層次並不一樣,比如,在Oracle中,可以在建表時指定將整個表置於緩存當中。

b) SESSION緩存:在一個Hibernate SESSION有效,這級緩存的可干預性不強,大多於HIBERNATE自動管理,但它提供清除緩存的方法,這在大批量增加/更新操作是有效的。比如,同時增加十萬條記錄,按常規方式進行,很可能會發現OutofMemeroy的異常,這時可能需要手動清除這一級緩存:Session.evict以及 Session.clear

c) 應用緩存:在一個SESSIONFACTORY中有效,因此也是優化的重中之重,因此,各類策略也考慮的較多,在將數據放入這一級緩存之前,需要考慮一些前提條件:

i. 數據不會被第三方修改(比如,是否有另一個應用也在修改這些數據?)

ii. 數據不會太大

iii. 數據不會頻繁更新(否則使用CACHE可能適得其反)

iv. 數據會被頻繁查詢

v. 數據不是關鍵數據(如涉及錢,安全等方面的問題)。

緩存有幾種形式,可以在映射文件中配置:read-only(只讀,適用於很少變更的靜態數據/歷史數據),nonstrict-read- write,read-write(比較普遍的形式,效率一般),transactional(JTA中,且支持的緩存產品較少)

d) 分散式緩存:同c)的配置一樣,只是緩存產品的選用不同,在目前的HIBERNATE中可供選擇的不多,oscache, jboss cache,目前的大多數項目,對它們的用於集群的使用(特別是關鍵交易系統)都持保守態度。在集群環境中,只利用資料庫級的緩存是最安全的。

5、 延遲載入

a) 實體延遲載入:通過使用動態代理實現

b) 集合延遲載入:通過實現自有的SET/LIST,HIBERNATE提供了這方面的支持

c) 屬性延遲載入:

6、 方法選用

a) 完成同樣一件事,Hibernate提供了可供選擇的一些方式,但具體使用什麼方式,可能用性能/代碼都會有影響。顯示,一次返回十萬條記錄 (List/Set/Bag/Map等)進行處理,很可能導致內存不夠的問題,而如果用基於游標(ScrollableResults)或 Iterator的結果集,則不存在這樣的問題。

b) Session的load/get方法,前者會使用二級緩存,而後者則不使用。

c) Query和list/iterator,如果去仔細研究一下它們,你可能會發現很多有意思的情況,二者主要區別(如果使用了Spring,在HibernateTemplate中對應find,iterator方法):

i. list只能利用查詢緩存(但在交易系統中查詢緩存作用不大),無法利用二級緩存中的單個實體,但list查出的對象會寫入二級緩存,但它一般只生成較少的執行SQL語句,很多情況就是一條(無關聯)。

ii. iterator則可以利用二級緩存,對於一條查詢語句,它會先從資料庫中找出所有符合條件的記錄的ID,再通過ID去緩存找,對於緩存中沒有的記錄,再構造語句從資料庫中查出,因此很容易知道,如果緩存中沒有任何符合條件的記錄,使用iterator會產生N+1條SQL語句(N為符合條件的記錄數)

iii. 通過iterator,配合緩存管理API,在海量數據查詢中可以很好的解決內存問題,如:

  while(it.hasNext()){
  YouObject object = (YouObject)it.next();
  session.evict(youObject);
  sessionFactory.evice(YouObject.class, youObject.getId());
  }

如果用list方法,很可能就出OutofMemory錯誤了。

iv. 通過上面的說明,我想你應該知道如何去使用這兩個方法了。

7、 集合的選用

在Hibernate 3.1文檔的「19.5. Understanding Collection performance」中有詳細的說明。

8、 事務控制

事務方面對性能有影響的主要包括:事務方式的選用,事務隔離級別以及鎖的選用

a) 事務方式選用:如果不涉及多個事務管理器事務的話,不需要使用JTA,只有JDBC的事務控制就可以。

b) 事務隔離級別:參見標準的SQL事務隔離級別

c) 鎖的選用:悲觀鎖(一般由具體的事務管理器實現),對於長事務效率低,但安全。樂觀鎖(一般在應用級別實現),如在HIBERNATE中可以定義 VERSION欄位,顯然,如果有多個應用操作數據,且這些應用不是用同一種樂觀鎖機制,則樂觀鎖會失效。因此,針對不同的數據應有不同的策略,同前面許多情況一樣,很多時候我們是在效率與安全/準確性上找一個平衡點,無論如何,優化都不是一個純技術的問題,你應該對你的應用和業務特徵有足夠的了解。

9、 批量操作

即使是使用JDBC,在進行大批數據更新時,BATCH與不使用BATCH有效率上也有很大的差別。我們可以通過設置batch_size來讓其支持批量操作。

舉個例子,要批量刪除某表中的對象,如「delete Account」,打出來的語句,會發現HIBERNATE找出了所有ACCOUNT的ID,再進行刪除,這主要是為了維護二級緩存,這樣效率肯定高不了,在後續的版本中增加了bulk delete/update,但這也無法解決緩存的維護問題。

 
上一篇[平兒]    下一篇 [YUV]

相關評論

同義詞:暫無同義詞