1. 程式人生 > >Hibernate 關係對映:雙向1:n(重要) 1:n n:1

Hibernate 關係對映:雙向1:n(重要) 1:n n:1

1.雙向1:n關聯(常用,非常重要)

對於1-N關聯,Hibernate推薦使用雙向關聯,而且不要讓1的一端控制關聯關係,而使用N的一端控制關聯關係。
雙向的N-1關聯與1-N關聯是完全相同的兩種情形。兩端都需要增加對關聯屬性的訪問,N的一端增加引用到關聯實體的屬性,1的一端增加集合屬性,集合元素為關聯實體。
採用一對多雙向關聯對映的目的主要是為了主要是為了解決一對多單向關聯的缺陷而不是需求驅動的。

一對多雙向關聯的對映方式:

         **在一的一端的集合上採用< key>標籤,在多的一端加入一個外來鍵**

         **在多的一端採用< many-to-one>標籤**

   **注意:**< key>標籤和< many-to-one>標籤加入的欄位保持一直,否則會產生資料混亂

域模型

從 Order 到 Customer 的多對一雙向關聯需要在Order 類中定義一個 Customer 屬性, 而在 Customer 類中需定義存放 Order 物件的集合屬性
這裡寫圖片描述

關係資料模型

ORDERS 表中的 CUSTOMER_ID 參照 CUSTOMER 表的主鍵
這裡寫圖片描述

注意

當Session從資料庫中載入Java集合時,建立的是Hibernate內建集合類的例項,因此,在持久化類中定義集合屬性時,必須把屬性宣告為Java介面
Hibernate的內建集合類具有集合代理功能,支援延遲檢索策略
事實上,Hibernate的內建集合類封裝了JDK中的集合類,這使得Hibernate能夠對快取中的集合物件進行髒檢查,按照集合物件的狀態來同步更新資料庫。
在定義集合屬性時,通常把它初始化為集合實現類的一個例項,這樣可以提高程式的健壯性,避免應用程式訪問取值為null的集合的方法。
例如:private Set orders = new HashSet< Order>();
Demo

實體物件

public class Customer {
    private Integer customerId;
    private String customerName;
    private Set<Order> orders = new HashSet<Order>();
    //省去get和set
}
public class Order {
    private Integer orderId;
    private String orderName;
    private Customer customer;
//省去get和set
}

Customer.hbm.xml

<hibernate-mapping package="com.lihui.hibernate.double_n_1">
    <class name="Customer" table="CUSTOMERS">
        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
        <property name="customerName" type="java.lang.String">
            <column name="CUSTOMER_NAME" />
        </property>
        <set name="orders"  inverse="true">
            <key column="CUSTOMER_ID"></key>
            <one-to-many class="Order"/>
        </set>
    </class>
</hibernate-mapping>

set

name屬性: 設定待對映的持久化類的屬性的
inverse 屬性:
在hibernate中通過對 inverse 屬性的來決定是由雙向關聯的哪一方來維護表和表之間的關係。 inverse = false 的為主動方,inverse = true 的為被動方, 由主動方負責維護關聯關係。在沒有設定 inverse=true 的情況下,父子兩邊都維護父子關係
在 1-N 關係中,將 N 方設為主控方將有助於效能改善(如果要國家元首記住全國人民的名字,不是太可能,但要讓全國人民知道國家元首,就容易的多)
在 1-N 關係中,若將 1 方設為主控方,會額外多出 update 語句。插入資料時無法同時插入外來鍵列,因而無法為外來鍵列新增非空約束.
order-by 屬性:
如果設定了該屬性, 當 Hibernate 通過 select 語句到資料庫中檢索集合物件時, 利用 order by 子句進行排序
order-by 屬性中還可以加入 SQL 函式例如:
這裡寫圖片描述

key

設定與所關聯的持久化類對應的表的外來鍵
column: 指定關聯表的外來鍵名

one-to-many

設定集合屬性中所關聯的持久化類
class: 指定關聯的持久化類的類名
Order.hbm.xml

<hibernate-mapping package="com.lihui.hibernate.double_n_1">
    <class name="Order" table="ORDERS">
        <id name="orderId" type="java.lang.Integer">
            <column name="ORDER_ID" />
            <generator class="native" />
        </id>
        <property name="orderName" type="java.lang.String">
            <column name="ORDER_NAME" />
        </property>
        <many-to-one name="customer" class="Customer" cascade="all" column="CUSTOMER_ID"></many-to-one>
    </class>
</hibernate-mapping>  

2.單向n:1關聯

原理:

多對一關聯對映原理:在多的一端加入一個外來鍵,指向一的一端

單向N-1關係,比如多個人對應一個地址,只需從人實體端可以找到對應的地址實體,無須關係某個地址的全部住戶。
單向 n-1 關聯只需從 n 的一端可以訪問 1 的一端。

域模型

從 Order 到 Customer 的多對一單向關聯需要在Order 類中定義一個 Customer 屬性, 而在 Customer 類中無需定義存放 Order 物件的集合屬性
這裡寫圖片描述

關係資料模型

ORDERS 表中的 CUSTOMER_ID 參照 CUSTOMER 表的主鍵
這裡寫圖片描述

Demo

Hibernate 使用 元素來對映多對一關聯關係< many-to-one name=”customer” class=”Customer” column=”CUSTOMER_ID” cascade=”all” />
name: 設定待對映的持久化類的屬性的名字
column: 設定和持久化類的屬性對應的表的外來鍵
class:設定待對映的持久化類的屬性的型別
cascade:意味著系統將先自動級聯插入主表記錄,也就是說先持久化Customer物件,再持久化Person物件。開發時不建議使用該屬性,建議使用手工的方式。
下面是實體物件,分為Customer(顧客)和Order(訂單),其中訂單和顧客是N-1關係。

public class Customer {
    private Integer customerId;
    private String customerName;
    //省去get和set方法
}
public class Order {
    private Integer orderId;
    private String orderName;
    private Customer customer;
        //省去get和set方法
}

Customer.hbm.xml 一的一方

<hibernate-mapping>
    <class name="com.lihui.hibernate.single_n_1.Customer" table="CUSTOMERS">
        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
        <property name="customerName" type="java.lang.String">
            <column name="CUSTOMER_NAME" />
        </property>
    </class>
</hibernate-mapping>

Order.hbm.xml 多的一方

<hibernate-mapping package="com.lihui.hibernate.single_n_1">
    <class name="Order"
        table="ORDERS">
        <id name="orderId" type="java.lang.Integer">
            <column name="ORDER_ID" />
            <generator class="native" />
        </id>
        <property name="orderName" type="java.lang.String">
            <column name="ORDER_NAME" />
        </property>
        <many-to-one name="customer" class="Customer" column="CUSTOMER_ID" cascade="all" />
    </class>
</hibernate-mapping>

Junit Test由於在Order.hbm.xml中配置了cascade=”all”,所以只需要儲存order物件即可,會首先自動儲存級聯的Customer物件。

@Test
    public void testSave() {
        Customer customer = new Customer();
        customer.setCustomerName("a");

        Order order1 = new Order();
        order1.setOrderName("A");
        order1.setCustomer(customer);
        Order order2 = new Order();
        order2.setOrderName("B");
        order2.setCustomer(customer);

        session.save(order1);
        session.save(order2);
    }

3.單向1:n關聯(有缺陷不常用)

一對多關聯對映和多對一關聯對映原理是一致的,都是在多的一端加入一個外來鍵,指向一的一端。
注意:它與多對一的區別是維護的關係不同

多對一維護的關係是:多指向一的關係,有了此關係,載入多的時候可以將一載入上來

一對多維護的關係是:一指向多的關係,有了此關係,在載入一的時候可以將多載入上來

關鍵對映程式碼——在一的一端加入如下標籤對映:

<set name="customer">  
      <key column="classesid"/>  
      <one-to-many class="com.hibernate.Customer"/>  
</set>  

缺陷:
因為多的一端Student不知道Classes的存在(也就是Student沒有維護與Classes的關係)所以在儲存Student的時候關係欄位classesid是為null的,如果將該關係欄位設定為非空,則將無法儲存資料,常用解決辦法是改用雙向關聯對映