Hibernate映射之基本類映射和對象關(guān)系映射詳解
回想一些我們在沒有學(xué)習(xí)ssh的時候,我們建立數(shù)據(jù)庫的表時,首先是數(shù)據(jù)庫建模E-R圖,然后再通過實體模型來建立關(guān)系模型,再建立相應(yīng)的表。實體間存在三種關(guān)系,一對一,一對多(或者說多對一),多對多。而如今我們要根據(jù)類來映射相應(yīng)的表,那只能是通過類與類之間的關(guān)系加上映射文件來映射數(shù)據(jù)庫的表。我們學(xué)習(xí)UML建模,類與類之間存在五種關(guān)系,繼承,實現(xiàn),關(guān)聯(lián),依賴,聚合/組合,在hibernate中實體類之間的關(guān)系也是如此,對于不同的關(guān)系對應(yīng)的代碼實現(xiàn)我們已經(jīng)很熟悉了,所以對于實體類是復(fù)習(xí)的知識。
Hibernate的本質(zhì)就是對象關(guān)系映射(ObjectRelational Mapping),ORM實現(xiàn)了將對象數(shù)據(jù)保存到數(shù)據(jù)庫中,以前我們對關(guān)系表進行操作,執(zhí)行增刪改查等任務(wù),現(xiàn)在我們不再對關(guān)系表進行操作,而是直接對對象操作。hibernate中的ORM映射文件通常以.hbm.xml作為后綴。使用這個映射文件不僅易讀,而且可以手工修改,也可以通過一些工具來生成映射文檔。下面將對hibernate中的映射進行介紹。
Hibernate映射分類,如下圖所示。
1 基本類映射
根據(jù)實體類創(chuàng)建相應(yīng)的表,這種簡單的關(guān)系為hibernate基本映射。
User1實體類代碼如下:
//user實體。 public classUser1 { //用戶編號。 private String id; //名字。 private String name; //密碼。 private String password; //創(chuàng)建日期。 private Date createTime; //失效時間。 private Date expireTime; public String getId() { return id; } // publicvoid setId(String id) { // this.id= id; // } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(Stringpassword) { this.password = password; } public Date getCreateTime() { return createTime; } public void setCreateTime(DatecreateTime) { this.createTime = createTime; } public Date getExpireTime() { return expireTime; } public void setExpireTime(DateexpireTime) { this.expireTime = expireTime; } }
User1.hbm.xml映射文件如下所示:
<hibernate-mapping package="com.bjpowernode.hibernate"> <class name="User1" table="t_user1"> <id name="id"column="user_id" length="32"access="field"> <generator class="uuid" /> </id> <!-- 設(shè)置主鍵不能重復(fù)和不能為空的屬性. --> <property name="name" length="30"unique="true" not-null="true"/> <property name="password"/> <property name="createTime" type="date" column="create_time"/> <property name="expireTime"/> </class> </hibernate-mapping>
通過User1.hbm.xml映射文件將User1對象轉(zhuǎn)換為關(guān)系數(shù)據(jù)庫中的表t_user1。
轉(zhuǎn)換出的結(jié)果如下所示:
2 對象關(guān)系映射
2.1 多對一關(guān)聯(lián)映射(單向)
例如用戶和組的關(guān)系就是多對一的關(guān)系,多個用戶對應(yīng)一個組。
將實體映射成表,將對應(yīng)的實體映射成表。對應(yīng)的屬性映射成表字段。
多對一關(guān)聯(lián)映射是在多的一端來維護關(guān)聯(lián)字段,在我們這個例子中也就是在用戶一端來維護關(guān)系字段。
User.hbm.xml文件。
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.User" table="t_user" > <id name="id"> <generator class="native" /> </id> <property name="name"/> <many-to-one name="group" column="groupid"cascade="save-update"></many-to-one> </class> </hibernate-mapping>
Group.hbm.xml文件。
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.Group" table="t_group"> <id name="id"> <generator class="native" /> </id> <property name="name"/> </class> </hibernate-mapping>
在這里我們看的代碼就看*.hbm.mlx代碼,因為對于類之間的關(guān)聯(lián),在實現(xiàn)時,一個類作為另一個類的私有成員,這一點在學(xué)UML建模的時候我們都懂了,在這里主要看的是ORM的M,也就是*.hbm.xml文件。
2.2 一對一關(guān)聯(lián)映射
一對一關(guān)聯(lián)映射在實際生活中是比較常見的,如人與家庭住址的關(guān)系,通過人這個對象可以找到他家庭住址相關(guān)的內(nèi)容。
2.2.1 一對一映射(單向主鍵關(guān)聯(lián))
單向一對一主鍵關(guān)聯(lián),靠的是它們的主鍵相等,從Person中能看到IdCard,也就是把t_idCard中的主鍵拿過來當做t_Pseron的主鍵。
Xml文件中:
<class name="com.bjpowernode.hibernate.Person"table="t_person" > <id name="id"> <!-- 采用foreign生成策略,foreign會取得關(guān)聯(lián)對象的標識 --> <generator class="foreign" > <!--property指的是關(guān)聯(lián)對象。 --> <param name="property">idCard</param> </generator> </id> <property name="name"/> <!-- 一對一關(guān)聯(lián)映射,主鍵關(guān)聯(lián). --> <!-- one-to-one標簽指示hibernate如何加載其關(guān)聯(lián)對象,默認根據(jù)主鍵加載. 也就是拿到關(guān)系字段值,根據(jù)對端的主鍵來加載關(guān)聯(lián)對象. constrained="true",表示當前主鍵(Person的主鍵)還是一個外鍵 . 參照了對端的主鍵(IdCard的主鍵),也就是會生成外鍵約束語句. --> <one-to-one name="idCard" constrained="true"/> </class>
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" > <id name="id"> <generator class="native" /> </id> <property name="cardNo"/> </class> </hibernate-mapping>
一對一的關(guān)系是通過one-to-one元素定義的。
2.2.2 一對一映射(雙向主鍵關(guān)聯(lián))
一對一雙向主鍵關(guān)聯(lián)與一對一單向主鍵關(guān)聯(lián)的區(qū)別就是,一對一單向主鍵關(guān)聯(lián),在person端能看到idCard,而idCard不能看到Person端。而雙向關(guān)聯(lián)就是在idCard端也能看到person,也就是不但在Person.hbm.xml中加上<one-to-one>標簽,同時在IdCard.hbm.xml文件中加上<one-to-one>標簽。代碼如下所示。
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" > <id name="id"> <generator class="native" /> </id> <property name="cardNo"/> <one-to-one name="person"/> </class> </hibernate-mapping>
2.2.3 一對一映射(單向唯一外鍵關(guān)聯(lián))
一對一單向唯一外鍵關(guān)聯(lián),也就是多對一關(guān)聯(lián)的特例,把多的一端限制為一,就是一對一唯一外鍵關(guān)聯(lián)。同多對一一樣,在一端加入另一端的并采用<many-to-one>標簽,通過unique="true",這樣來限制了多的一端為一。
先上代碼。
IdCard.hbm.xml
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" > <id name="id"> <generator class="native" /> </id> <property name="cardNo"/> </class> </hibernate-mapping>
Person.hbm.xml
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.Person" table="t_person" > <id name="id"> <!-- 采用foreign生成策略,foreign會取得關(guān)聯(lián)對象的標識 --> <generator class="native" /> </id> <property name="name"/> <many-to-one name="idCard" unique="true"></many-to-one> </class> </hibernate-mapping>
圖如下所示:
在t_pserson端加上一個外鍵字段idCard,限制idCard的唯一性就是一對一唯一外鍵關(guān)聯(lián)。
2.2.4 一對一映射(雙向唯一外鍵關(guān)聯(lián))
一對一唯一外鍵單向關(guān)聯(lián)我們已經(jīng)了解了,雙向反過來就是在沒有的一端加上就可以了。
我們的IdCard.hbm.xml中采用<one-to-one>標簽。
<hibernate-mapping package="org.hibernate.auction"> <class name="com.bjpowernode.hibernate.IdCard" table="t_idCard" > <id name="id"> <generator class="native" /> </id> <property name="cardNo"/> <one-to-one name="person" property-ref="idCard"></one-to-one> </class> </hibernate-mapping>
而person.hbm.xml同一對一唯一外鍵單向關(guān)聯(lián)一樣。
<class name="com.bjpowernode.hibernate.Person" table="t_person" > <id name="id"> <!-- 采用foreign生成策略,foreign會取得關(guān)聯(lián)對象的標識 --> <generator class="native" /> </id> <property name="name"/> <many-to-one name="idCard" unique="true"></many-to-one> </class>
從上述中可以總結(jié)出,對于一對一關(guān)聯(lián)映射,主鍵關(guān)聯(lián)和唯一外鍵關(guān)聯(lián)單向和雙向產(chǎn)生出的表結(jié)構(gòu)是一樣的,不同的是在加載的時候不同。也就是一對一雙向關(guān)聯(lián)和一對一單向關(guān)聯(lián)的相比,只是改變了一對一關(guān)聯(lián)映射的加載,而沒有改變存儲。
2.3 一對多關(guān)聯(lián)映射
2.3.1 一對多關(guān)聯(lián)映射(單向)
上面我們介紹了多對一,我們反過來看一對多不就是多對一嗎?那還用再進行不同的映射嗎?有什么差別嗎?一對多和多對一映射原理是一致的,存儲是相同的,也就是生成的數(shù)據(jù)庫的表是一樣的,他們之間不同的是維護的關(guān)系不同。
他們之間不同點是維護的關(guān)系不同
- 多對一維護的關(guān)系是:多指向一的關(guān)系,有了此關(guān)系,加載多的時候可以將一加載上來。
- 一對多維護的關(guān)系是:一指向多的關(guān)系,有了此關(guān)系,在加載一的時候可以將多加載上來。
代碼如下所示。
Class.hbm.xml
<class name="com.bjpowernode.hibernate.Classes" table="t_Classes" > <id name="id"> <generator class="native" /> </id> <property name="name"/> <set name="students"> <!-- <keycolumn="classesid" not-null="true"/> --> <key column="classesid" /> <one-to-many class="com.bjpowernode.hibernate.Student"/> </set> </class>
Students.hbm.xml
<class name="com.bjpowernode.hibernate.Student" table="t_student" > <id name="id"> <generator class="native" /> </id> <property name="name"/> </class>
從班級能看到學(xué)生,是班級來維護關(guān)系,不是學(xué)生來維護關(guān)系,學(xué)生不知道自己是哪個班,所以在存儲學(xué)生的時候,班級的代碼不知道。為了更新學(xué)生是哪個班級的要發(fā)出很多update語句來告訴學(xué)生是哪個班級的。當我們設(shè)置classesid not-null=“true”時,則將無法保存數(shù)據(jù),解決辦法我們改為雙向關(guān)聯(lián)映射。
2.3.2 一對多關(guān)聯(lián)映射(雙向)
為了解決一對多單向可能存在的問題,我們采用雙向一對多,每一方都能維護對方。
一對多雙向關(guān)聯(lián)映射方式:
- 在一的一端的集合上采用<key>標簽,在多的一端加入一個外鍵。
- 在多的一端采用<many-to-one>的標簽
!~注意<key>標簽和<many-to-one>標簽加入字段保持一致,否則會產(chǎn)生數(shù)據(jù)混亂。
代碼如下所示。
<class name="com.bjpowernode.hibernate.Classes" table="t_Classes" > <id name="id"> <generator class="native" /> </id> <property name="name"/> <set name="students" inverse="true"> <!-- <keycolumn="classesid" not-null="true"/> --> <key column="classesid" /> <one-to-many class="com.bjpowernode.hibernate.Student"/> </set> </class>
<class name="com.bjpowernode.hibernate.Student" table="t_student" > <id name="id"> <generator class="native" /> </id> <property name="name"/> <many-to-one name="classes"column="classesid"/> </class>
注意:Inverse屬性
1、 Inverse中文意思為相反的,反轉(zhuǎn)。在hibernate中inverse可以用在一對多和多對多雙向關(guān)聯(lián)上,inverse默認是false,為false的時候表示本端可以維護關(guān)系,如果inverse為true,則本端不能維護關(guān)系,會交給另一端維護關(guān)系,本端失效,所以在一對多關(guān)聯(lián)映射我們通常在多的一端維護關(guān)系,讓一的一端失效。
2、Inverse是控制方向上的反轉(zhuǎn),只影響存儲。
比較一對多單向和雙向映射,從存儲結(jié)構(gòu)上看沒有什么區(qū)別,但是從配置文件上看,一對多雙向比一對多單向,一對多雙向關(guān)聯(lián)的配置文件中在多的一端的配置文件上存在<many-to-one>相關(guān)配置,即保證多對一的映射。
2.4 多對多關(guān)聯(lián)映射
2.4.1 多對多關(guān)聯(lián)映射(單向)
多對多對象關(guān)系映射,需要加入一張新表完成基本映射。如下圖所示。
代碼。
Role.hbm.xml
<class name="com.bjpowernode.hibernate.Role" table="t_role"> <id name="id"> <generator class="native" /> </id> <property name="name"/> </class>
User.hbm.xml
<class name="com.bjpowernode.hibernate.User" table="t_user" > <id name="id"> <generator class="native" /> </id> <property name="name"/> <set name="roles" table="t_user_role"> <key column="user_id"/> <many-to-many class="com.bjpowernode.hibernate.Role" column="role_id"/> </set> </class>
2.4.2 多對多關(guān)聯(lián)映射(雙向)
雙向多對多對象關(guān)系映射,是兩端都能將對方加載上來,雙向都需要加上標簽映射。
要注意:
- 生成中間表名必須一樣
- 生成中間表字段必須一樣
代碼如下所示。
Role.hbm.xml
<class name="com.bjpowernode.hibernate.Role" table="t_role"> <id name="id"> <generator class="native" /> </id> <property name="name"/> <set name="users" table="t_user_role"> <key column="role_id"/> <many-to-many class="com.bjpowernode.hibernate.User" column="user_id"/> </set> </class>
User.hbm.xml
<class name="com.bjpowernode.hibernate.User"table="t_user" > <id name="id"> <generator class="native" /> </id> <property name="name"/> <set name="roles" table="t_user_role"> <key column="user_id"/> <many-to-many class="com.bjpowernode.hibernate.Role" column="role_id"/> </set> </class>
區(qū)別:單向多對多和雙向多對多存儲結(jié)構(gòu)沒有任何的區(qū)別,但他們的映射文件是有區(qū)別的,加載過程是不同的。
3 關(guān)系映射總結(jié)
綜上所述,可以看出,同一類映射,無論是單向還是雙向,他們的存儲結(jié)構(gòu)是相同的,之所以映射文件不同,是因為加載時不同(在增刪改時)。
無論是多對一、一對多、一對一還是多對一,A對B,A就是主動方,A主動想要了解B的情況,這樣把B設(shè)置到A端。而雙向,也就是A對B,A想了解B的信息,而B也想了解A的信息,那就要同時把A設(shè)置到B端了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
spring使用RedisTemplate操作Redis數(shù)據(jù)庫
這篇文章主要介紹了spring使用RedisTemplate操作Redis數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Java對數(shù)組實現(xiàn)選擇排序算法的實例詳解
這篇文章主要介紹了Java對數(shù)組實現(xiàn)選擇排序算法的實例,選擇排序的比較次數(shù)為 O(N^2)而交換數(shù)為O(N),需要的朋友可以參考下2016-04-04簡單講解Java的Socket網(wǎng)絡(luò)編程的多播與廣播實現(xiàn)
這篇文章主要介紹了Java的Socket網(wǎng)絡(luò)編程的多播與廣播實現(xiàn),包括網(wǎng)絡(luò)編程發(fā)送和接受數(shù)據(jù)的一些基礎(chǔ)知識整理,需要的朋友可以參考下2016-01-01使用Spring源碼報錯java:找不到類 InstrumentationSavingAgent的問題
這篇文章主要介紹了使用Spring源碼報錯java:找不到類 InstrumentationSavingAgent的問題,本文給大家分享解決方法,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10Springboot利于第三方服務(wù)進行ip定位獲取省份城市
本文主要介紹了Springboot利于第三方服務(wù)進行ip定位獲取省份城市,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Mybatis-Plus實現(xiàn)公共字段自動填充的項目實踐
本文主要介紹了Mybatis-Plus實現(xiàn)公共字段自動填充的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07