(一)映射組成關係
· 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)三種映射方式比較