一、Hibernate的數據檢索策略是面向對象的策略,對與不同的檢索情況,應使用不同的策略,以提高效率節省資源。經常使用的是立即檢索和延遲檢索,預先抓取和批量檢索都是為了優化加載性能而設計的策略。檢索一般是建立在表關聯的基礎上的所以我們先建立三張表Team與Student是一對多關系,Student和Certificate是一對一關系。
Team→id:String、teamName:String
Student→id:String、team_id、name、cardid、age
Certificate→id:String、describe
在這里Team相當于原對象Student相當于Team的附屬物,Certificate相當于Student的附屬物。當加載一個原對象時,原對象對附屬物的使用有2中情況:(1)馬上就用附屬物的數據(2)可能一會用,可能不用。第一種情況適用于立即檢索,第二種情況適用于延遲檢索。
1.立即檢索
立即檢索的配置文件標識符為lazy="false"。
一般情況下我們把一對一和多對一這樣后者為一的對象用于立即檢索,因為是“一”的這段,所帶來的性能消耗也有限。立即裝載附屬物有個好處,原對象脫離管理成為VO以后,仍然可以使用這些附屬物。而對一對多和多對多關聯對象則不適合立即檢索。比如班級和學生是一對多的關系,當取得班級對象時,如過是立即檢索,就會把班級的所有學生對象組裝起來,這種資源消耗在這沒有必要。
2.延遲檢索
延遲檢索的配置文件標識符為
lazy="true"。
延遲檢索就是等到需要用到時(比如調用了Team.getTeamName()。調用stu.getTeam()不行,必須以Team為主體取得附屬物時,才會實現延遲加載)才會去取被關聯對象Team。因此如果在Student對象的配置文件中設置了對Team進行延遲檢索,則在取的學生1對象時,Hibernate僅把學生1對象設置為立即檢索的對象和屬性裝配進學生1對象,而并不會立即去組裝設為延遲檢索的附屬物,只有在
同一個Session中使用到班級1時,Hibernate才會從數據庫中查找數據并裝配成班級1對象。
如果把班級對象納入另一個Session中,使用org.hibernate.Hibernate這個類的initialize()方法,這時脫離Session管理仍然可以從班級對象中取出數據。
3.預先抓取
預先抓取的配置文件標識符為
fetch="join"
如果把三個表之間的關系都設為立即檢索,取得班級對象時,從而遞歸的得到學生和身份證對象。如果有100個學生,則要發送100條SQL語句去取100個身份證。這樣會很大程度的降低性能。所以必須減少發送的SQL語句的數量。使用預先抓取只是用一條SQL語句就能代替立即檢索的100條SQL語句。再具體點比如有一堆貨物,原先有100個人排隊順序去背,現在讓一個人一次背回來,反而影響性能。每次背的數量可用hibernate.max_fetch_depth來控制。還有一點需要注意,HQL使用外連接查詢時忽略配置文件中配置的預先抓取策略,如:Query q = session .createQuery("from Strudent as s left join s.team");所以我們應該改為Query q = session .createQuery("from Strudent as s left join
fetch s.team");
僅從使用角度來說。預先抓取和立即檢索的效果一樣,只不過預先抓取能減少SQL語句的條數。
4.批量加載
批量加載總是和立即或延遲加載聯系在一起,分為批量立即加載和批量延遲加載。主要是控制發送SQL語句的條數,較少資源的消耗。
對于一對多、多對多在集合的配置中設置,以set為例:
<set name = "students" inverse="true" batch-size="3">
<key column="team_id"/>
<one-to-many class="model.Student"/>
</set>
一對一、多對一比如學生對班級是多對一關系,要實現對班級的批量加載,如下:
<class name="model.Team" table="team" batch-size="3">
二、下面分析一下.hbm.xml文件
有3個實體,Team、Student、Certificate,Team與Student是一對多關系,Student和Certificate是一對一。
(1).Student.hbm.xml配置文件源碼:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
<hibernate-mapping>
5
<class name="model.Student" table="student" lazy="true">
6
<id name="id" unsaved-value="null">
7
<generator class="uuid.hex"/>
8
</id>
9
<property name="cardid" type="string"/><!--映射號-->
10
<property name="name" type="string"/><!--映射學生名-->
11
<property name="age" type="int"/><!--映射學生年齡-->
12
<one-to-one name="cer" class="model.Certificate" fetch="join" cascade="all"/><!--映射對應的身份證對象-->
13
<many-to-one name="team" class="model.Team" column="team_id" fetch="join"/><!--映射班級-->
14
</class>
15
</hibernate-mapping>
Student.hbm.xml配置文件信息的解釋如下:
第5行:model.Student類對應的數據庫表是student表。lazy="true"當使用一對一、多對一方式加載Student類時,將采用延遲加載(前提是設置預先抓取為false)。
第6、7、8行:Student類有一個主鍵id,id由Hibernate自動生成,生成算法是uuid.hex。在內存中有很多對象,可以通過unsaved-value
="null"來判斷對象是持久化的還是臨時狀態。當對象的id值為unsaved-value指定的"null"時,認為未持久化,否則認為此對象是持久化或托管狀態。注意int、long型的主鍵id的默認unsaved-value="0"。
第12行:一個一對一關聯的對象屬性,名為cer,fetch="join"在得到Student對象時。采用預先抓取得到cer對象。cascade="all"表示級聯為all,說明Student的crud操作都會影響cer對象。
第13行:一個多對一關聯的對象屬性,名為Team,在Student表中通過team_id與Team對象發生關聯,引用team中id成外鍵,沒個Student對象對應唯一的team。team的實體類是model.Team。在取得Student實例時。使用預先抓取得到team對象。
(2).Certificate.hbm.xml配置文件源碼:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
<hibernate-mapping>
5
<class name="model.Certificate" table="certificate" lazy="true">
6
<id name="id">
7
<generator class="foreign">
8
<param name="property">stu</param>
9
</generator>
10
</id>
11
<one-to-one name="stu" class="model.Student" fetch="join" constrained="true">
12
</one-to-one>
13
<property name="describe" column="describes" type="string"></property>
14
</class>
15
</hibernate-mapping>
Certificate.hbm.xml配置文件信息的解釋如下:
第6、7、8、9、10行:Certificate和Student是一對一關聯,使用的是以主鍵關聯,即Certificate和Student公用一個主鍵值。Certificate的主鍵id生成使用外鍵方式(
generator class="foreign"),此外鍵參考一對一引用中的"stu"變量。
第11行:一對一關聯model.Student類,當取Certificate對象時,預先抓取Student類(fetch="join"),聲明了constrained
="true"。這個選項影響save()和delete()在級聯執行的先后順序以及決定該關聯類能否被延遲加載(true時,此是的Student類才可被延遲加載)。
(3).Team.hbm.xml配置文件源碼:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
<hibernate-mapping>
5
<class name="model.Team" table="team" lazy="true">
6
<id name="id" unsaved-value="null">
7
<generator class="uuid.hex">
8
</generator>
9
</id>
10
<property name="teamName" type="string"/>
11
<set name="students" inverse="true" fetch="select" lazy="true">
12
<key column="team_id"/>
13
<one-to-many class="model.Student"/>
14
</set>
15
</class>
16
</hibernate-mapping>
Team.hbm.xml配置文件信息的解釋如下:
第11、12、13、14行:一個Set集合名為students,采用延遲加載,在實體對象Student對象的student表中。一team_id列對應Team對象。
(4)需要在說一下的問題:
cascade屬性的用法:
save-update:在執行save/update/saveOrUpdate時進行關聯操作。
delete:在執行delete時進行關聯操作。
none:所有情況下均不進行關聯操作。這是默認值。
all的意思是save-update + delete 。
all-delete-orphan 的意思是當對象圖中產生孤兒節點時,在數據庫中刪除該節點 。
舉個例子說一下all-delete-orphan,Category與Item是一對多的關系,也就是說Category類中有個Set類型的變量items.
舉個例子,現items中存兩個Item, item1,item2,如果定義關系為all-delete-orphan ,當items中刪除掉一個item(比如用remove()方法刪除item1),那么被刪除的Item類實例 ,將變成孤兒節點,當執行category.update(),或session.flush()時
hibernate同步緩存和數據庫,會把數據庫中item1對應的記錄刪掉。
在多對多關系中不可以把cascade設置為all和delete,這樣問題就大了,想想會有什么后果。
unique屬性的用法:
以外間關聯的一對一,本質上變成了一對多的雙向關聯了,編寫以一對多和多對一要求編寫,最后在many-to-one這一邊加上unique="true"即說明它只不過是一對多的特例(“多”這一方只有一個對象)。
constrained屬性的用法:
對one-to-one關系進行延遲加載和其他關系相比稍微有些不同。many-to-one的延遲加載是在配置文件的class標簽設置lazy="true",one-to-many和many-tomany的延遲加載在Set標簽設置lazy="true",而one-to-one不只要在class標簽設置lazy="true",而且要在one-to-one標簽中設置constrained="true"。也就是說one-to-one關系,constrained="true"和附屬物的lazy="true"才能實現延遲加載,并忽略默認的預先抓取。(這里說的是默認,如果指定fetch="join",則無論如何都是預先抓取)。
inverse屬性的用法:
兩個表設置了雙相關聯,inverse="true"表示雙方的關系將由對方去維護。
一對多應該讓“多”的這一端有主動權,也就是在“一”這一端設置inverse="true"。
多對多關聯關系中,如果雙方都有控制權,這時會有一些問題,比如雙方都引起插入語句,這就會違反數據庫的主鍵約束,原因是試圖插入重復主鍵。說以在某一方設置inverse="true",然后在執行語句時使用主控方進行操作(也就是沒有設置inverse="true"的一方)。這樣就解決了。
本文章只是本人在學習中總結的自己的一些知識,希望各位看到不足和錯誤之處給予指出,我會加以改正。在此先致謝了。