再開始本次知識點之前,我們先來思考下get與load的區(qū)別。
- 對于Hibernate get方法,Hibernate會確認(rèn)一下該id對應(yīng)的數(shù)據(jù)是否存在,首先在session緩存中查找,然后在二級緩存中查找,還沒有就查詢數(shù)據(jù)庫,數(shù)據(jù) 庫中沒有就返回null。
- Hibernate load方法加載實體對象的時候,根據(jù)映射文件上類級別的lazy屬性的配置(默認(rèn)為true),分情況討論:
(1)若為true,則首先在Session緩存中查找,看看該id對應(yīng)的對象是否存在,不存在則使用延遲加載,返回實體的代理類對象(該代理類為實體類的子類,由CGLIB動態(tài)生成)。等到具體使用該對象(除獲取OID以外)的時候,再查詢二級緩存和數(shù)據(jù)庫,若仍沒發(fā)現(xiàn)符合條件的記錄,則會拋出一個ObjectNotFoundException。
(2)若為false,就跟Hibernateget方法查找順序一樣,只是最終若沒發(fā)現(xiàn)符合條件的記錄,則會拋出一個ObjectNotFoundException。
這里get和load有兩個重要區(qū)別:
如果未能發(fā)現(xiàn)符合條件的記錄,Hibernate get方法返回null,而load方法會拋出一個ObjectNotFoundException。
load方法可返回沒有加載實體數(shù)據(jù)的代 理類實例,而get方法永遠(yuǎn)返回有實體數(shù)據(jù)的對象。
總之對于get和load的根本區(qū)別,一句話,hibernate對于 load方法認(rèn)為該數(shù)據(jù)在數(shù)據(jù)庫中一定存在,可以放心的使用代理來延遲加載,如果在使用過程中發(fā)現(xiàn)了問題,只能拋異常;而對于get方 法,hibernate一定要獲取到真實的數(shù)據(jù),否則返回null。
七種映射關(guān)系
hibernate在實現(xiàn)ORM功能的時候主要用到的文件有:映射類(.Java)、映射文件(.hbm.xml)和數(shù)據(jù)庫配置文件(.properties/.cfg.xml),它們各自的作用如下。
映射類(*.java):它是描述數(shù)據(jù)庫表的結(jié)構(gòu),表中的字段在類中被描述成屬性,將來就可以實現(xiàn)把表中的記錄映射成為該類的對象了。
映射文件(*.hbm.xml):它是指定數(shù)據(jù)庫表和映射類之間的關(guān)系,包括映射類和數(shù)據(jù)庫表的對應(yīng)關(guān)系、表字段和類屬性類型的對應(yīng)關(guān)系以及表字段和類屬性名稱的對應(yīng)關(guān)系等。
> 數(shù)據(jù)庫配置文件(*.properties/*.cfg.xml):它是指定與數(shù)據(jù)庫連接時需要的連接信息,比如連接哪種數(shù)據(jù)庫、登錄數(shù)據(jù)庫的用戶名、登錄密碼以及連接字符串等。當(dāng)然還可以把映射類的地址映射信息放在這里。
接下來讓我們就一起走進(jìn)Hibernate的七種映射關(guān)系:
1、 單向一對一關(guān)聯(lián)映射(one-to-one):
兩個對象之間一對一的關(guān)系,例如:Person(人)-IdCard(身份證)
有兩種策略可以實現(xiàn)一對一的關(guān)聯(lián)映射:
主鍵關(guān)聯(lián):即讓兩個對象具有相同的主鍵值,以表明它們之間的一一對應(yīng)的關(guān)系;數(shù)據(jù)庫表不會有額外的字段來維護(hù)它們之間的關(guān)系,僅通過表的主鍵來關(guān)聯(lián)。如下圖:
單向一對一主鍵關(guān)聯(lián)例子連接
唯一外鍵關(guān)聯(lián):外鍵關(guān)聯(lián),本來是用于多對一的配置,但是加上唯一的限制之后(采用<many-to-one>標(biāo)簽來映射,指定多的一端unique為true,這樣就限制了多的一端的多重性為一),也可以用來表示一對一關(guān)聯(lián)關(guān)系,其實它就是多對一的特殊情況。如下圖:
單向一對一唯一外鍵關(guān)聯(lián)例子連接
注意:因為一對一的主鍵關(guān)聯(lián)映射擴(kuò)展性不好,當(dāng)我們的需要發(fā)生改變想要將其變?yōu)橐粚Χ嗟臅r候變無法操作了,所以我們遇到一對一關(guān)聯(lián)的時候經(jīng)常會采用唯一外鍵關(guān)聯(lián)來解決問題,而很少使用一對一主鍵關(guān)聯(lián)。
2、單向多對一關(guān)聯(lián)映射(many-to-one):
多對一關(guān)聯(lián)映射原理:在多的一端加入一個外鍵,指向一的一端,如下圖:
單向多對一關(guān)聯(lián)映射
關(guān)鍵映射代碼——在多的一端加入如下標(biāo)簽映射:
<many-to-one name="group" column="groupid"/>
3、單向一對多關(guān)聯(lián)映射(one-to-many):
一對多關(guān)聯(lián)映射和多對一關(guān)聯(lián)映射原理是一致的,都是在多的一端加入一個外鍵,指向一的一端。如下圖(學(xué)生和班級):
單向一對多關(guān)聯(lián)映射
注意:它與多對一的區(qū)別是維護(hù)的關(guān)系不同
多對一維護(hù)的關(guān)系是:多指向一的關(guān)系,有了此關(guān)系,加載多的時候可以將一加載上來
一對多維護(hù)的關(guān)系是:一指向多的關(guān)系,有了此關(guān)系,在加載一的時候可以將多加載上來
關(guān)鍵映射代碼——在一的一端加入如下標(biāo)簽映射:
<set name="students">
<key column="classesid"/>
<one-to-many class="com.hibernate.Student"/>
</set>
缺陷:因為多的一端Student不知道Classes的存在(也就是Student沒有維護(hù)與Classes的關(guān)系)所以在保存Student的時候關(guān)系字段classesid是為null的,如果將該關(guān)系字段設(shè)置為非空,則將無法保存數(shù)據(jù),常用解決辦法是改用雙向關(guān)聯(lián)映射,參見6。
4、單向多對多映射(many-to-many):
多對多關(guān)聯(lián)映射新增加一張表才完成基本映射,如下圖:
單向多對多映射
關(guān)鍵映射代碼——可以在User的一端加入如下標(biāo)簽映射:
<set name="roles" table="t_user_role">
<key column="user_id"/>
<many-to-many class="com.hibernate.Role" column="role_id"/>
</set>
5、雙向一對一關(guān)聯(lián)映射:
對比單向一對一映射,需要在IdCard加入<one-to-one>標(biāo)簽,它不影響,只影響加載。如下圖:
雙向一對一關(guān)聯(lián)映射
雙向一對一主鍵映射關(guān)鍵映射代碼——在IdCard端新加入如下標(biāo)簽映射:
<one-to-one name="person"/>
雙向一對一唯一外鍵映射關(guān)鍵映射代碼——在IdCard端新加入如下標(biāo)簽映射:
<one-to-one name="person"property-ref="idCard"/>
注意:一對一唯一外鍵關(guān)聯(lián)雙向采用<one-to-one>標(biāo)簽映射,必須指定<one-to-one>標(biāo)簽中的property-ref屬性為關(guān)系字段的名稱
6、雙向一對多關(guān)聯(lián)映射(非常重要):
采用一對多雙向關(guān)聯(lián)映射的目的主要是為了主要是為了解決一對多單向關(guān)聯(lián)的缺陷而不是需求驅(qū)動的。
一對多雙向關(guān)聯(lián)的映射方式:
<li>在一的一端的集合上采用<key>標(biāo)簽,在多的一端加入一個外鍵
<li>在多的一端采用<many-to-one>標(biāo)簽
>注意:<key>標(biāo)簽和<many-to-one>標(biāo)簽加入的字段保持一直,否則會產(chǎn)生數(shù)據(jù)混亂
>關(guān)鍵映射代碼:
> 在Classes的一端加入如下標(biāo)簽映射:
<set name="students"inverse="true">
<key column="classesid"/>
<one-to-many class="com.hibernate.Student"/>
</set>
在Student的一端加入如下標(biāo)簽映射:
<many-to-one name="classes" column="classesid"/>
注釋:inverse屬性
<li> inverse屬性可以用在一對多和多對多雙向關(guān)聯(lián)上,inverse屬性默認(rèn)為false,為false表示本端可以維護(hù)關(guān)系,如果inverse為true,則本端不能維護(hù)關(guān)系,會交給另一端維護(hù)關(guān)系,本端失效。所以一對多關(guān)聯(lián)映射我們通常在多的一端維護(hù)關(guān)系,讓一的一端失效。
<li> inverse是控制方向上的反轉(zhuǎn),只影響存儲
7、雙向多對多關(guān)聯(lián)映射:
雙向的目的就是為了兩端都能將對方加載上來,和單向多對多的區(qū)別就是雙向需要在兩端都加入標(biāo)簽映射,需要注意的是:
<li>生成的中間表名稱必須一樣
<li>生成的中間表中的字段必須一樣
Role(角色)端關(guān)鍵映射代碼:
<set name="users" table="t_user_role">
<key column="role_id"/>
<many-to-many class="com.hibernate.User" column="user_id"/>
lt;/set>
User(用戶)端關(guān)鍵映射代碼:
<set name="roles" table="t_user_role">
<key column="user_id"/>
<many-to-many class="com. hibernate.Role" column="role_id"/>
lt;/set>
總結(jié):對于上面這七種關(guān)聯(lián)映射中,最重要的就是一對多的映射,因為它更貼近我們的現(xiàn)實生活,比如:教室和學(xué)生就可以是典型的一對多的關(guān)系,而我們開發(fā)軟件的目的之一就是為了解決一些生活中重復(fù)性問題,把那些重復(fù)的問題交給計算機幫助我們完成,從而來提高我們的工作效率。





