Hibernate(四)---映射關係

(一)映射組成關係
這裏寫圖片描述
· Hibernate把持久化類的屬性分爲兩種:

  • 值(value)類型:沒有 OID, 不能被單獨持久化, 生命週期依賴於所屬的持久化類的對象的生命週期.
  • 實體(entity)類型: 有 OID, 可以被單獨持久化, 有獨立的生命週期.
    · Hibernate 使用 <component> 元素來映射組成關係, 該元素表名 pay 屬性是 Worker 類一個組成部分, 在 Hibernate 中稱之爲組件.
    · 配置文件:
    這裏寫圖片描述
    例:
hibernate-mapping package="com.ty.hibernate.entities">
    <class name="Worker" table="WORKER">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <!-- 映射組成關係 -->
        <component name="pay" class="Pay">
            <parent name="worker"/>
            <!-- 指定組成關係的組件的屬性 -->
            <property name="monthlyPay" column="MONTHLY_PAY"></property>
            <property name="yearPay" column="YEAR_PAY"></property>
            <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property>
        </component>

    </class>
</hibernate-mapping>

(二)映射一對多關聯關係
(1)單向n–1
· 單向 n-1 關聯只需從 n 的一端可以訪問 1 的一端;
· 域模型: 從 Order 到 Customer 的多對一單向關聯需要在Order 類中定義一個 Customer 屬性, 而在 Customer 類中無需定義存放 Order 對象的集合屬性;
這裏寫圖片描述
· 關係數據模型:ORDERS 表中的 CUSTOMER_ID 參照 CUSTOMER 表的主鍵
這裏寫圖片描述
· Hibernate使用 <many-to-one> 元素來映射多對一關聯關係

<!-- 

            name: 多這一端關聯的一那一端的屬性的名字
            class: 一那一端的屬性對應的類名
            column: 一那一端在多的一端對應的數據表中的外鍵的名字
-->
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>

(2)雙向n–1
· 雙向 1-n 與 雙向 n-1 是完全相同的兩種情形.
· 雙向 1-n 需要在 1 的一端可以訪問 n 的一端, 反之依然.
· 域模型:從 Order 到 Customer 的多對一雙向關聯需要在Order 類中定義一個 Customer 屬性, 而在 Customer 類中需定義存放 Order 對象的集合屬性.
這裏寫圖片描述
· 關係數據模型:ORDERS 表中的 CUSTOMER_ID 參照 CUSTOMER 表的主鍵.
這裏寫圖片描述
· 在持久化類中定義集合屬性時必須把屬性聲明爲 Java 接口類型
· 在定義集合屬性時, 通常把它初始化爲集合實現類的一個實例. 這樣可以提高程序的健壯性, 避免應用程序訪問取值爲 null 的集合的方法拋出 NullPointerException

private Set<Order> order = new HashSet<>();

· Hibernate 使用 <set> 元素來映射 set 類型的屬性

        <!-- 映射 1 對多的那個集合屬性 -->
        <!-- set: 映射 set 類型的屬性, table: set 中的元素對應的記錄放在哪一個數據表中. 該值需要和多對一的多的那個表的名字一致 -->
        <!-- inverse: 指定由哪一方來維護關聯關係. 通常設置爲 true, 以指定由多的一端來維護關聯關係 -->
        <!-- cascade 設定級聯操作. 開發時不建議設定該屬性. 建議使用手工的方式來處理 -->
        <!-- order-by 在查詢時對集合中的元素進行排序, order-by 中使用的是表的字段名, 而不是持久化類的屬性名,order-by 屬性中還可以加入 SQL 函數
  -->
        <set name="orders" table="ORDERS" inverse="true" order-by="ORDER_NAME DESC">
            <!-- 執行多的表中(即指定Order表的外鍵列名)的外鍵列的名字 -->
            <key column="CUSTOMER_ID"></key>
            <!-- 指定映射類型 -->
            <one-to-many class="Order"/>
        </set>

注:cascade 屬性
· 在對象 – 關係映射文件中, 用於映射持久化類之間關聯關係的元素, <set>, <many-to-one> 和 <one-to-one> 都有一個 cascade 屬性, 它用於指定如何操縱與當前對象關聯的其他對象
這裏寫圖片描述

注:
若三個表之間通過一對多映射(即Customer表一對多映射Order表(Order表中含有Customer的外鍵Customer_Id),Order表一對多映射DetailOrder(DetailOrder表中含有Order的外鍵Order_ID)),這種情況下只能通過鏈接三個表才能將數據映射到DetailOrder對象的各個屬性(詳情查看51Shop項目中JavaBean模型的SuperType,SubType及Goods的映射關係)

//通過Hql查詢
String hql = "From DetailOrder d Join Fetch d.orderId o join fetch o.customerId "
List<DetailOrder> detailOrder = getSession().createQuery(hql).list()

(三)映射一對一關聯關係
(1)基於外鍵映射的一對一
· 關係數據模型
這裏寫圖片描述
· 對於基於外鍵的1-1關聯,其外鍵可以存放在任意一邊,在需要存放外鍵一端,增加many-to-one元素。爲many-to-one元素增加unique=“true” 屬性來表示爲1-1關聯.

<many-to-one name="manager" class="com.ty.model2.oneToOne.foregin.Manager"
          >
          <column name="MANAGER_ID" unique="true"/>
        </many-to-one>

· 另一端需要使用one-to-one元素,該元素使用 property-ref 屬性指定使用被關聯實體主鍵以外的字段作爲關聯字段

<one-to-one name="deparment" class="com.ty.model2.oneToOne.foregin.Deparment" property-ref="manager" ></one-to-one>

注:若不使用property-ref屬性:
這裏寫圖片描述
(2)基於主鍵映射的一對一
· 關係數據模型
這裏寫圖片描述
· 基於主鍵的映射策略:指一端的主鍵生成器使用 foreign 策略,表明根據”對方”的主鍵來生成自己的主鍵,自己並不能獨立生成主鍵. <param> 子元素指定使用當前持久化類的哪個屬性作爲 “對方”.

//Deparment表
    <class name="Department" table="DEPARTMENTS">

        <id name="deptId" type="java.lang.Integer">
            <column name="DEPT_ID" />
            <!-- 使用外鍵的方式來生成當前的主鍵 -->
            <generator class="foreign">
                <!-- property 屬性指定使用當前持久化類的哪一個屬性的主鍵作爲外鍵 -->
                <param name="property">mgr</param>
            </generator>
        </id>

        <property name="deptName" type="java.lang.String">
            <column name="DEPT_NAME" />
        </property>

        <!--  
        採用 foreign 主鍵生成器策略的一端增加 one-to-one 元素映射關聯屬性,
        其 one-to-one 節點還應增加 constrained=true 屬性, 以使當前的主鍵上添加外鍵約束
        -->
        <one-to-one name="mgr" class="Manager" constrained="true"></one-to-one>
//Manager表  
   <class name="com.atguigu.hibernate.one2one.primary.Manager" table="MANAGERS">

        <id name="mgrId" type="java.lang.Integer">
            <column name="MGR_ID" />
            <generator class="native" />
        </id>

        <property name="mgrName" type="java.lang.String">
            <column name="MGR_NAME" />
        </property>

        <one-to-one name="dept" 
            class="com.atguigu.hibernate.one2one.primary.Department"></one-to-one>

    </class>

(四)映射多對多關聯關係
(1)單項n-n關係
· 關係數據模型
這裏寫圖片描述
· n-n 的關聯必須使用連接表.
· 與 1-n 映射類似,必須爲 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中參照 CATEGORIES 表的外鍵爲 CATEGORIY_ID. 與 1-n 關聯映射不同的是,建立 n-n 關聯時, 集合中的元素使用 many-to-many. many-to-many 子元素的 class 屬性指定 items 集合中存放的是 Item 對象, column 屬性指定 CATEGORIES_ITEMS 表中參照 ITEMS 表的外鍵爲 ITEM_ID.

 <!-- table: 指定中間表 -->
        <set name="items" table="CATEGORIES_ITEMS">
            <key>
                <column name="C_ID" />
            </key>
            <!-- 使用 many-to-many 指定多對多的關聯關係. column 執行 Set 集合中的持久化類在中間表的外鍵列的名稱  -->
            <many-to-many class="Item" column="I_ID"></many-to-many>
        </set>

(2)雙向n-n關係
· 關係數據模型
這裏寫圖片描述
· 雙向 n-n 關聯需要兩端都使用集合屬性
· 雙向n-n關聯必須使用連接表
· 集合屬性應增加 key 子元素用以映射外鍵列, 集合元素裏還應增加many-to-many子元素關聯實體類
· 在雙向 n-n 關聯的兩邊都需指定連接表的表名及外鍵列的列名. 兩個集合元素 set 的 table 元素的值必須指定,而且必須相同。set元素的兩個子元素:key 和 many-to-many 都必須指定 column 屬性,其中,key 和 many-to-many 分別指定本持久化類和關聯類在連接表中的外鍵列名,因此兩邊的 key 與 many-to-many 的column屬性交叉相同。也就是說,一邊的set元素的key的 cloumn值爲a,many-to-many 的 column 爲b;則另一邊的 set 元素的 key 的 column 值 b,many-to-many的 column 值爲 a.
· 對於雙向 n-n 關聯, 必須把其中一端的 inverse 設置爲 true, 否則兩端都維護關聯關係可能會造成主鍵衝突.
(五)繼承映射
(1)採用 subclass 元素的繼承映射
· 採用 subclass 的繼承映射可以實現對於繼承關係中父類和子類使用同一張表
· 因爲父類和子類的實例全部保存在同一個表中,因此需要在該表內增加一列,使用該列來區分每行記錄到低是哪個類的實例—-這個列被稱爲辨別者列(discriminator).
· 在這種映射策略下,使用 subclass 來映射子類,使用 class 或 subclass 的 discriminator-value 屬性指定辨別者列的值
· 所有子類定義的字段都不能有非空約束。如果爲那些字段添加非空約束,那麼父類的實例在那些列其實並沒有值,這將引起數據庫完整性衝突,導致父類的實例無法保存到數據庫中
這裏寫圖片描述

<hibernate-mapping>
    <class name="com.ty.model2.subclass.Person" table="PERSONS" discriminator-value="Person">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <discriminator column="TYPE" type="java.lang.String"/>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="AGE" />
        </property>
        <subclass name="com.ty.model2.subclass.Student" discriminator-value="STUDENT">
          <property name="school" type="java.lang.String"/>
        </subclass>
    </class>
</hibernate-mapping>

(2)採用 joined-subclass 元素的繼承映射
· 採用 joined-subclass 元素的繼承映射可以實現每個子類一張表
· 採用這種映射策略時,父類實例保存在父類表中,子類實例由父類表和子類表共同存儲。因爲子類實例也是一個特殊的父類實例,因此必然也包含了父類實例的屬性。於是將子類和父類共有的屬性保存在父類表中,子類增加的屬性,則保存在子類表中。
· 在這種映射策略下,無須使用鑑別者列,但需要爲每個子類使用 key 元素映射共有主鍵。
· 子類增加的屬性可以添加非空約束。因爲子類的屬性和父類的屬性沒有保存在同一個表中。
這裏寫圖片描述

<hibernate-mapping>
    <class name="com.ty.model2.joined.Person" table="PERSON">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="AGE" />
        </property>
        <joined-subclass name="com.ty.model2.joined.Student">
           <key column="STUDENT_ID" not-null="true"></key>
           <property name="school" type="java.lang.String"/>
        </joined-subclass>
    </class>
</hibernate-mapping>

(3)採用 union-subclass 元素的繼承映射
· 採用 union-subclass 元素可以實現將每一個實體對象映射到一個獨立的表中。
· 子類增加的屬性可以有非空約束 — 即父類實例的數據保存在父表中,而子類實例的數據保存在子類表中。
· 子類實例的數據僅保存在子類表中, 而在父類表中沒有任何記錄
· 在這種映射策略下,子類表的字段會比父類表的映射字段要多,因爲子類表的字段等於父類表的字段、加子類增加屬性的總和
· 在這種映射策略下,既不需要使用鑑別者列,也無須使用 key 元素來映射共有主鍵.
· 使用 union-subclass 映射策略是不可使用 identity 的主鍵生成策略, 因爲同一類繼承層次中所有實體類都需要使用同一個主鍵種子, 即多個持久化實體對應的記錄的主鍵應該是連續的. 受此影響, 也不該使用 native 主鍵生成策略, 因爲 native 會根據數據庫來選擇使用 identity 或 sequence.
這裏寫圖片描述

<hibernate-mapping>
    <class name="com.ty.model2.union.Person" table="PERSON">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="hilo" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="AGE" />
        </property>

        <union-subclass name="com.ty.model2.union.Student">
           <property name="school" type="java.lang.String"/>
        </union-subclass>
    </class>
</hibernate-mapping>

(4)三種映射方式比較
這裏寫圖片描述

發佈了43 篇原創文章 · 獲贊 5 · 訪問量 4832
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章