Hibernate是一個開放源代碼的對象關系映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與數據庫表建立映射關系,是一個全自動的orm框架,hibernate可以自動生成SQL語句,自動執行,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。 Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。——《百度百科》
簡單來說Hibernate就是一個用于JSP的數據庫操作的工具集,可以方便地對數據庫進行操作,將數據庫基本操作轉化為面向對象的操作,而且Hibernate實現了跨數據庫操作,可以在使用不同的數據庫項目中使用,使程序猿可以專注于項目邏輯的實現,從冗長的數據庫命令中解脫出來。
核心接口和類
Hibernate的核心類和接口一共有6個,分別為:Session、SessionFactory、Transaction、Query、Criteria和Configuration。這6個核心類和接口在任何開發中都會用到。通過這些接口,不僅可以對持久化對象進行存取,還能夠進行事務控制。下面對這6個核心接口和類分別加以介紹。
Session
Session接口負責執行被持久化對象的CRUD操作(CRUD的任務是完成與數據庫的交流,包含了很多常見的SQL語句)。但需要注意的是Session對象是非線程安全的。同時,Hibernate的session不同于JSP應用中的HttpSession。這里當使用session這個術語時,其實指的是Hibernate中的session,而以后會將HttpSession對象稱為用戶session。
SessionFactory
SessionFactory接口負責初始化Hibernate。它充當數據存儲源的代理,并負責創建Session對象。這里用到了工廠模式。需要注意的是SessionFactory并不是輕量級的,因為一般情況下,一個項目通常只需要一個SessionFactory就夠,當需要操作多個數據庫時,可以為每個數據庫指定一個SessionFactory。
Transaction
Transaction 接口是一個可選的API,可以選擇不使用這個接口,取而代之的是Hibernate 的設計者自己寫的底層事務處理代碼。 Transaction 接口是對實際事務實現的一個抽象,這些實現包括JDBC的事務、JTA 中的UserTransaction、甚至可以是CORBA 事務。之所以這樣設計是能讓開發者能夠使用一個統一事務的操作界面,使得自己的項目可以在不同的環境和容器之間方便地移植。
Query
Query接口讓你方便地對數據庫及持久對象進行查詢,它可以有兩種表達方式:HQL語言或本地數據庫的SQL語句。Query經常被用來綁定查詢參數、限制查詢記錄數量,并最終執行查詢操作。
Criteria
Criteria接口與Query接口非常類似,允許創建并執行面向對象的標準化查詢。值得注意的是Criteria接口也是輕量級的,它不能在Session之外使用。
Configuration
Configuration 類的作用是對Hibernate 進行配置,以及對它進行啟動。在Hibernate 的啟動過程中,Configuration 類的實例首先定位映射文檔的位置,讀取這些配置,然后創建一個SessionFactory對象。雖然Configuration 類在整個Hibernate 項目中只扮演著一個很小的角色,但它是啟動hibernate 時所遇到的第一個對象。
主鍵介紹
Assigned
Assigned方式由用戶生成主鍵值,并且要在save()之前指定否則會拋出異常
特點:主鍵的生成值完全由用戶決定,與底層數據庫無關。用戶需要維護主鍵值,在調用session.save()之前要指定主鍵值。
Hilo
Hilo使用高低位算法生成主鍵,高低位算法使用一個高位值和一個低位值,然后把算法得到的兩個值拼接起來作為數據庫中的唯一主鍵Hilo方式需要額外的數據庫表和字段提供高位值來源。默認情況下使用的表是hibernate_unique_key,默認字段叫作next_hi。next_hi必須有一條記錄否則會出現錯誤。
特點:需要額外的數據庫表的支持,能保證同一個數據庫中主鍵的唯一性,但不能保證多個數據庫之間主鍵的唯一性。Hilo主鍵生成方式由Hibernate 維護,所以Hilo方式與底層數據庫無關,但不應該手動修改hi/lo算法使用的表的值,否則會引起主鍵重復的異常。
Increment
Increment方式對主鍵值采取自動增長的方式生成新的主鍵值,但要求底層數據庫的主鍵類型為long,int等數值型。主鍵按數值順序遞增,增量為1。
特點:由Hibernate本身維護,適用于所有的數據庫,不適合多進程并發更新數據庫,適合單一進程訪問數據庫。不能用于群集環境。
Identity
Identity方式根據底層數據庫,來支持自動增長,不同的數據庫用不同的主鍵增長方式。
特點:與底層數據庫有關,要求數據庫支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的數據庫有MySql、SQL Server、DB2、Sybase和HypersonicSQL。 Identity無需Hibernate和用戶的干涉,使用較為方便,但不便于在不同的數據庫之間移植程序。
Sequence
Sequence需要底層數據庫支持Sequence方式,例如Oracle數據庫等
特點:需要底層數據庫的支持序列,支持序列的數據庫有DB2、PostgreSql、Oracle、SAPDb等在不同數據庫之間移植程序,特別從支持序列的數據庫移植到不支持序列的數據庫需要修改配置文件。
Native
Native主鍵生成方式會根據不同的底層數據庫自動選擇Identity、Sequence、Hilo主鍵生成方式
特點:根據不同的底層數據庫采用不同的主鍵生成方式。由于Hibernate會根據底層數據庫采用不同的映射方式,因此便于程序移植,項目中如果用到多個數據庫時,可以使用這種方式。
UUID
UUID使用128位UUID算法生成主鍵,能夠保證網絡環境下的主鍵唯一性,也就能夠保證在不同數據庫及不同服務器下主鍵的唯一性。
特點:能夠保證數據庫中的主鍵唯一性,生成的主鍵占用比較多的存貯空間
Foreign GUID
Foreign用于一對一關系中。GUID主鍵生成方式使用了一種特殊算法,保證生成主鍵的唯一性,支持SQL Server和MySQL
緩存管理
Hibernate 中提供了兩級Cache(高速緩沖存儲器),第一級別的緩存是Session級別的緩存,它是屬于事務范圍的緩存。這一級別的緩存由hibernate管理的,一般情況下無需進行干預;第二級別的緩存是SessionFactory級別的緩存,它是屬于進程范圍或集群范圍的緩存。這一級別的緩存可以進行配置和更改,并且可以動態加載和卸載。 Hibernate還為查詢結果提供了一個查詢緩存,它依賴于第二級緩存。
第一級緩存 第二級緩存 存放數據的形式 相互關聯的持久化對象的散裝數據 緩存的范圍
事務范圍,每個事務都有單獨的第一級緩存進程范圍或集群范圍,緩存被同一個進程或集群范圍內的所有事務共享 并發訪問策略由于每個事務都擁有單獨的第一級緩存,不會出現并發問題,無需提供并發訪問策略由于多個事務會同時訪問第二級緩存中相同數據,因此必須提供適當的并發訪問策略,來保證特定的事務隔離級別數據過期策略沒有提供數據過期策略。處于一級緩存中的對象永遠不會過期,除非應用程序顯式清空緩存或者清除特定的對象必須提供數據過期策略,如基于內存的緩存中的對象的最大數目,允許對象處于緩存中的最長時間,以及允許對象處于緩存中的最長空閑時間。
物理存儲介質內存和硬盤 對象的散裝數據首先存放在基于內存的緩存中,當內存中對象的數目達到數據過期策略中指定上限時,就會把其余的對象寫入基于硬盤的緩存中。
緩存的軟件實現 在Hibernate的Session的實現中包含了緩存的實現由第三方提供,Hibernate僅提供了緩存適配器(CacheProvider)。用于把特定的緩存插件集成到Hibernate中。啟用緩存的方式只要應用程序通過Session接口來執行保存、更新、刪除、加載和查詢數據庫數據的操作,Hibernate就會啟用第一級緩存,把數據庫中的數據以對象的形式拷貝到緩存中,對于批量更新和批量刪除操作,如果不希望啟用第一級緩存,可以繞過Hibernate API,直接通過JDBC API來執行指操作。用戶可以在單個類或類的單個集合的粒度上配置第二級緩存。如果類的實例被經常讀但很少被修改,就可以考慮使用第二級緩存。只有為某個類或集合配置了第二級緩存,Hibernate在運行時才會把它的實例加入到第二級緩存中。 用戶管理緩存的方式第一級緩存的物理介質為內存,由于內存容量有限,必須通過恰當的檢索策略和檢索方式來限制加載對象的數目。Session的evit()方法可以顯式清空緩存中特定對象,但這種方法不值得推薦。 第二級緩存的物理介質可以是內存和硬盤,因此第二級緩存可以存放大量的數據,數據過期策略的maxElementsInMemory屬性值可以控制內存中的對象數目。管理第二級緩存主要包括兩個方面:選擇需要使用第二級緩存的持久類,設置合適的并發訪問策略:選擇緩存適配器,設置合適的數據過期策略。
一級緩存
當應用程序調用Session的save()、update()、saveOrUpdate()、get()或load(),以及調用查詢接口的 list()、iterate()或filter()方法時,如果在Session緩存中還不存在相應的對象,Hibernate就會把該對象加入到第一級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新數據庫。 Session為應用程序提供了兩個管理緩存的方法: evict(Object obj):從緩存中清除參數指定的持久化對象。 clear():清空緩存中所有持久化對象。
二級緩存
Hibernate的二級緩存策略的一般過程如下:
1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有字段)這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。
2) 把獲得的所有數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
4) 刪除、更新、增加數據的時候,同時更新緩存。
Hibernate的二級緩存策略,是針對于ID查詢的緩存策略,對于條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query Cache。
什么樣的數據適合存放到第二級緩存中??
1 很少被修改的數據?
2 不是很重要的數據,允許出現偶爾并發的數據?
3 不會被并發訪問的數據 4 參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其他類的實例引用,實例極少或者從來不會被修改。
不適合存放到第二級緩存的數據??
1 經常被修改的數據
2 財務數據,絕對不允許出現并發?
3 與其他應用共享的數據。
常用的緩存插件 Hibernater 的二級緩存是一個插件,下面是幾種常用的緩存插件:
EhCache:可作為進程范圍的緩存,存放數據的物理介質可以是內存或硬盤,對Hibernate的查詢緩存提供了支持。
OSCache:可作為進程范圍的緩存,存放數據的物理介質可以是內存或硬盤,提供了豐富的緩存數據過期策略,對Hibernate的查詢緩存提供了支持。
SwarmCache:可作為群集范圍內的緩存,但不支持Hibernate的查詢緩存。
JBossCache:可作為群集范圍內的緩存,支持事務型并發訪問策略,對Hibernate的查詢緩存提供了支持。
上述4種緩存插件的對比情況
在默認情況下,Hibernate使用EhCache進行JVM級別的緩存。用戶可以通過設置Hibernate配置文件中的hibernate.cache.provider_class的屬性,指定其他的緩存策略,該緩存策略必須實現org.hibernate.cache.CacheProvider接口。
配置二級緩存的主要步驟:
1) 選擇需要使用二級緩存的持久化類,設置它的命名緩存的并發訪問策略。這是最值得認真考慮的步驟。
2) 選擇合適的緩存插件,然后編輯該插件的配置文件。
延遲加載
延遲加載
Hibernate對象關系映射提供延遲的與非延遲的對象初始化。非延遲加載在讀取一個對象的時候會將與這個對象所有相關的其他對象一起讀取出來。這有時會導致成百的(如果不是成千的話)select語句在讀取對象的時候執行。這個問題有時出現在使用雙向關系的時候,經常會導致整個數據庫都在初始化的階段被讀出來了。當然,你可以不厭其煩地檢查每一個對象與其他對象的關系,并把那些最昂貴的刪除,但是到最后,我們可能會因此失去了本想在ORM工具中獲得的便利。
一個明顯的解決方法是使用Hibernate提供的延遲加載機制。這種初始化策略只在一個對象調用它的一對多或多對多關系時才將關系對象讀取出來。這個過程對開發者來說是透明的,而且只進行了很少的數據庫操作請求,因此會得到比較明顯的性能提升。這項技術的一個缺陷是延遲加載技術要求一個Hibernate會話要在對象使用的時候一直開著。這會成為通過使用DAO模式將持久層抽象出來時的一個主要問題。為了將持久化機制完全地抽象出來,所有的數據庫邏輯,包括打開或關閉會話,都不能在應用層出現。最常見的是,一些實現了簡單接口的DAO實現類將數據庫邏輯完全封裝起來了。一種快速但是笨拙的解決方法是放棄DAO模式,將數據庫連接邏輯加到應用層中來。這可能對一些小的應用程序有效,但是在大的系統中,這是一個嚴重的設計缺陷,妨礙了系統的可擴展性。
Web層延遲加載
幸運的是,Spring框架為Hibernate延遲加載與DAO模式的整合提供了一種方便的解決方法。以一個Web應用為例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我們可以隨意選擇一個類來實現相同的功能。兩種方法唯一的不同就在于interceptor在Spring容器中運行并被配置在web應用的上下文中,而Filter在Spring之前運行并被配置在web.xml中。不管用哪個,他們都在請求將當前會話與當前(數據庫)線程綁定時打開Hibernate會話。一旦已綁定到線程,這個打開了的Hibernate會話可以在DAO實現類中透明地使用。這個會話會為延遲加載數據庫中值對象的視圖保持打開狀態。一旦這個邏輯視圖完成了,Hibernate會話會在Filter的doFilter方法或者Interceptor的postHandle方法中被關閉。
性能優化
初用HIBERNATE的人也許都遇到過性能問題,實現同一功能,用HIBERNATE與用JDBC性能相差十幾倍很正常,如果不及早調整,很可能影響整個項目的進度。 大體上,對于HIBERNATE性能調優的主要考慮點如下:
.數據庫設計調整
.HQL優化
.API的正確使用(如根據不同的業務類型選用不同的集合及查詢API)
.主配置參數(日志,查詢緩存,fetch_size, batch_size等)
.映射文件優化(ID生成策略,二級緩存,延遲加載,關聯優化)
.一級緩存的管理
.針對二級緩存,還有許多特有的策略
.事務控制策略。
數據庫設計
a) 降低關聯的復雜性
b) 盡量不使用聯合主鍵
c) ID的生成機制,不同的數據庫所提供的機制并不完全一樣
d) 適當的冗余數據,不過分追求高范式
HQL優化
HQL如果拋開它同HIBERNATE本身一些緩存機制的關聯,HQL的優化技巧同普通的SQL優化技巧一樣,可以很容易在網上找到一些經驗之談。
主配置
a) 查詢緩存,同下面講的緩存不太一樣,它是針對HQL語句的緩存,即完全一樣的語句再次執行時可以利用緩存數據。但是,查詢緩存在一個交易系統(數據變更頻繁,查詢條件相同的機率并不大)中可能會起反作用:它會白白耗費大量的系統資源但卻難以派上用場。
b) fetch_size,同JDBC的相關參數作用類似,參數并不是越大越好,而應根據業務特征去設置
c) batch_size同上。
d) 生產系統中,切記要關掉SQL語句打印。
緩存
a) 數據庫級緩存:這級緩存是最高效和安全的,但不同的數據庫可管理的層次并不一樣,比如,在Oracle中,可以在建表時指定將整個表置于緩存當中。
b) SESSION緩存:在一個HibernateSESSION有效,這級緩存的可干預性不強,大多于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)的配置一樣,只是緩存產品的選用不同,oscache, jboss cache,的大多數項目,對它們的用于集群的使用(特別是關鍵交易系統)都持保守態度。在集群環境中,只利用數據庫級的緩存是最安全的。
延遲加載
a) 實體延遲加載:通過使用動態代理實現
b) 集合延遲加載:通過實現自有的SET/LIST,HIBERNATE提供了這方面的支持
c) 屬性延遲加載:
方法選用
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錯誤了。
集合的選用
在Hibernate3.1文檔的“19.5. Understanding Collection performance”中有詳細的說明。
事務控制
事務方面對性能有影響的主要包括:事務方式的選用,事務隔離級別以及鎖的選用
a) 事務方式選用:如果不涉及多個事務管理器事務的話,不需要使用JTA,只有JDBC的事務控制就可以。
b) 事務隔離級別:參見標準的SQL事務隔離級別
c) 鎖的選用:悲觀鎖(一般由具體的事務管理器實現),對于長事務效率低,但安全。樂觀鎖(一般在應用級別實現),如在HIBERNATE中可以定義 VERSION字段,顯然,如果有多個應用操作數據,且這些應用不是用同一種樂觀鎖機制,則樂觀鎖會失效。因此,針對不同的數據應有不同的策略,同前面許多情況一樣,很多時候我們是在效率與安全/準確性上找一個平衡點,無論如何,優化都不是一個純技術的問題,你應該對你的應用和業務特征有足夠的了解。
批量操作
即使是使用JDBC,在進行大批數據更新時,BATCH與不使用BATCH有效率上也有很大的差別。可以通過設置batch_size來讓其支持批量操作。
舉個例子,要批量刪除某表中的對象,如“delete Account”,打出來的語句,HIBERNATE找出了所有ACCOUNT的ID,再進行刪除,這主要是為了維護二級緩存,這樣效率肯定高不了,在后續的版本中增加了bulk delete/update,但這也無法解決緩存的維護問題。也就是說,由于有了二級緩存的維護問題,HIBERNATE的批量操作效率并不盡如人意。
hibernate工作原理:
1、通過Configuration().configure();讀取并解析hibernate.cfg.xml配置文件。
2、由hibernate.cfg.xml中的讀取解析映射信息。
3、通過config.buildSessionFactory();//得到sessionFactory。
4、sessionFactory.openSession();//得到session。
5、session.beginTransaction();//開啟事務。
6、persistent operate;
7、session.getTransaction().commit();//提交事務
8、關閉session;
9、關閉sessionFactory;
hibernate優點:
1、封裝了jdbc,簡化了很多重復性代碼。
2、簡化了DAO層編碼工作,使開發更對象化了。
3、移植性好,支持各種數據庫,如果換個數據庫只要在配置文件中變換配置就可以了,不用改變hibernate代碼。
4、支持透明持久化,因為hibernate操作的是純粹的(pojo)java類,沒有實現任何接口,沒有侵入性。所以說它是一個輕量級框架。
hibernate延遲加載:
get不支持延遲加載,load支持延遲加載。
1、hibernate2對 實體對象和集合 實現了延遲加載
2、hibernate3對 提供了屬性的延遲加載功能
hibernate延遲加載就是當使用session.load(User.class,1)或者session.createQuery()查詢對象或者屬性的時候
這個對象或者屬性并沒有在內存中,只有當程序操作數據的時候,才會存在內存中,這樣就實現延遲加載,節省了內存的開銷,從而提高了服務器的性能。
Hibernate的緩存機制
一級緩存:session級的緩存也叫事務級的緩存,只緩存實體,生命周期和session一致。不能對其進行管理。不用顯示的調用。
二級緩存:sessionFactory緩存,也叫進程級的緩存,使用第3方插件實現的,也值緩存實體,生命周期和sessionFactory一致,可以進行管理。
首先配置第3方插件,我們用的是EHCache,在hibernate.cfg.xml文件中加入
在映射中也要顯示的調用,二級緩存之查詢緩存:對普通屬性進行緩存。如果關聯的表發生了修改,那么查詢緩存的生命周期也結束了。
在程序中必須手動啟用查詢緩存:query.setCacheable(true);
優化Hibernate
1、使用一對多的雙向關聯,盡量從多的一端維護。
2、不要使用一對一,盡量使用多對一。
3、配置對象緩存,不要使用集合緩存。
4、表字段要少,表關聯不要怕多,有二級緩存撐腰。
hibernate 類與類之間關系
關聯關系
聚集關系
繼承關系
Hibernate繼承關系映射策略分為三種:
一張表對應一整棵類繼承樹、一個類對應一張表、每一個具體類對應一張表。