Hibernate —— 映射關聯關係

来源:http://www.cnblogs.com/solverpeng/archive/2016/10/12/5948461.html
-Advertisement-
Play Games

一、映射多對一關聯關係。 1.單向的多對一 (1)以 Customer 和 Order 為例:一個用戶可以發出多個訂單,而一個訂單隻能屬於一個客戶。從 Order 到 Customer 是多對一關聯關係。 (2)創建 Customer 和 Order 表。 CREATE TABLE customer ...


一、映射多對一關聯關係。

1.單向的多對一

(1)以 Customer 和 Order 為例:一個用戶可以發出多個訂單,而一個訂單隻能屬於一個客戶。從 Order 到 Customer 是多對一關聯關係。

(2)創建 Customer 和 Order 表。

CREATE TABLE customer (
  customer_id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  customer_name VARCHAR(50)
)


CREATE TABLE `order` (
  order_id    INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
  order_name  VARCHAR(50),
  customer_id INT(11)
)
Create

(3)用 Intellij Idea 自動生成關聯關係,以及對應的 Entitiy.hbm.xml 和 持久化類。

說明:

其中 Type 是用來修飾對應的 Attribute Name 的。

在 Order 端,定義 Customer 類,一個訂單屬於一個客戶。而在 Customer 端,一個客戶可以有多個訂單,因為是單向的,所以這裡放棄屬性的添加。

在 Join Columns 定義了 Order 和 Customer 之間的關聯關係,order 表中的 customer_id 外鍵和 customer 表中的 customer_id 主鍵關聯。

來看生成的 Schema:

沒有勾選 customer_id,是因為 Intellij Idea 沒法直接映射為 Customer 類型的 customer。

<hibernate-mapping>

    <class name="com.nucsoft.hibernate.Order" table="order" schema="hibernate">
        <id name="orderId">
            <column name="order_id" sql-type="int(11)"/>
            <generator class="native"/>
        </id>
        <property name="orderName">
            <column name="order_name" sql-type="varchar(50)" length="50" not-null="true"/>
        </property>
        <many-to-one name="customer" class="com.nucsoft.hibernate.Customer">
            <column name="customer_id" not-null="true"/>
        </many-to-one>
    </class>
</hibernate-mapping>
Order.hbm.xml

使用 <many-to-one> 節點來維護多對一關聯關係。

name 屬性:多這一端關聯的一那一端的屬性的名稱。

class 屬性:關聯的一端的屬性的類型。

column 屬性:一那一端在多的一端對應的數據表中的外鍵。可以任意命名,但需要和數據表中的欄位對應。

(4)單向多對一的 CRUD 以及需要註意的問題。

<1> 新增

①先保存一的一端 Customer,後保存多的一端 Order。

@Test
public void testMany2OneSave() {
    Customer customer = new Customer();
    customer.setCustomerName("aa");

    Order order = new Order();
    order.setOrderName("order1");
    order.setCustomer(customer);

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

    session.save(customer);
    session.save(order);
    session.save(order2);
}
Save.java

列印 SQL:

Hibernate: 
    insert 
    into
        hibernate.customer
        (customer_name) 
    values
        (?)
Hibernate: 
    insert 
    into
        hibernate.order
        (order_name, customer_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        hibernate.order
        (order_name, customer_id) 
    values
        (?, ?)
Output

結論:發送了3條 INSERT 語句。

②先保存多的一端 Order,再保存一的一端 Customer。

@Test
public void testMany2OneSave() {
    Customer customer = new Customer();
    customer.setCustomerName("bb");

    Order order = new Order();
    order.setOrderName("order3");
    order.setCustomer(customer);

    Order order2 = new Order();
    order2.setOrderName("order4");
    order2.setCustomer(customer);

    session.save(order);
    session.save(order2);
    session.save(customer);
}
Save2.java

列印 SQL:

Hibernate: 
    insert 
    into
        hibernate.order
        (order_name, customer_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        hibernate.order
        (order_name, customer_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        hibernate.customer
        (customer_name) 
    values
        (?)
Hibernate: 
    update
        hibernate.order 
    set
        order_name=?,
        customer_id=? 
    where
        order_id=?
Hibernate: 
    update
        hibernate.order 
    set
        order_name=?,
        customer_id=? 
    where
        order_id=?
Output2

結論:發送了3條 INSERT 語句,2條 UPDATE 語句。

總結:在單向多對一的關聯關係下,先插入 1 的一端會減少 SQL 語句的執行,性能更高。

<2>刪除

先刪除1的一端。

@Test
public void testMany2OneDelete() {
    Customer customer = (Customer) session.get(Customer.class, 1);
    session.delete(customer);
} 
Delete.java

控制台列印:

Cannot delete or update a parent row: a foreign key constraint fails (`hibernate`.`order`, CONSTRAINT `FK_m6q2ofkj1g5aobtb2p00ajpqg` FOREIGN KEY (`customer_id`)  REFERENCES `customer` (`customer_id`))

結論:在不設置級聯關係的前提下,不能刪除 1 的一端。

<3>更新

@Test
public void testMany2OneUpdate() {
    Order order = (Order) session.get(Order.class, 1);
    order.getCustomer().setCustomerName("aaa");
}
Update.java
Hibernate: 
    select
        order0_.order_id as order_id1_1_0_,
        order0_.order_name as order_na2_1_0_,
        order0_.customer_id as customer3_1_0_ 
    from
        hibernate.order order0_ 
    where
        order0_.order_id=?
Hibernate: 
    select
        customer0_.customer_id as customer1_0_0_,
        customer0_.customer_name as customer2_0_0_ 
    from
        hibernate.customer customer0_ 
    where
        customer0_.customer_id=?
Hibernate: 
    update
        hibernate.customer 
    set
        customer_name=? 
    where
        customer_id=?
Output

<4>查詢

①查詢 n 的一端,但是不使用查詢出來關聯的 1 的一端的對象。

@Test
public void testMany2OneGet() {
    Order order = (Order) session.get(Order.class, 1);
    System.out.println(order.getCustomer().getClass().getName());
} 
Hibernate: 
    select
        order0_.order_id as order_id1_1_0_,
        order0_.order_name as order_na2_1_0_,
        order0_.customer_id as customer3_1_0_ 
    from
        hibernate.order order0_ 
    where
        order0_.order_id=?
order1
com.nucsoft.hibernate.Customer_$$_jvst30c_1

②查詢 n 的一端,使用查詢出來關聯的 1 的一端的對象。

@Test
public void testMany2OneGet() {
    Order order = (Order) session.get(Order.class, 1);
    System.out.println(order.getCustomer().getClass().getName());
    order.getCustomer().getCustomerName();
}
Hibernate: 
    select
        order0_.order_id as order_id1_1_0_,
        order0_.order_name as order_na2_1_0_,
        order0_.customer_id as customer3_1_0_ 
    from
        hibernate.order order0_ 
    where
        order0_.order_id=?
com.nucsoft.hibernate.Customer_$$_jvst30c_1
Hibernate: 
    select
        customer0_.customer_id as customer1_0_0_,
        customer0_.customer_name as customer2_0_0_ 
    from
        hibernate.customer customer0_ 
    where
        customer0_.customer_id=?

總結:可以發現,採用的是懶載入機制,即獲取到的 1 的一端的對象是一個代理對象。只有在使用這個對象的屬性的情況下,才會發送 SQL 語句。

③ 由懶載入機制引發的 懶載入異常。

@Test
public void testMany2OneGet() {
    Order order = (Order) session.get(Order.class, 1);
    System.out.println(order.getCustomer().getClass().getName());
    session.close();
    order.getCustomer().getCustomerName();
}
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

在需要使用對象之前,關閉了 Session 連接,由此會引發 LazyInitializationException 異常。

2.雙向的多對一

(1)還是以 Order 和 Customer 為例:雙向的多對一不僅僅要在 Order 類中定義一個 Customer 屬性,而在 Customer 類中也需定義存放 Order 對象的集合屬性。

(2)創建 Order  和 Customer 表和創建單向多對一相同。

(3)通過 Intellij Idea 生成簡單的持久化類和 Entity.hbm.xml 文件。手動的去建立關聯關係。

<1>生成簡單的持久化類 和 Entity.hbm.xml 文件

package com.nucsoft.hibernate;

/**
 * @author solverpeng
 * @create 2016-10-11-13:23
 */
public class Customer {
    private Integer customerId;
    private String customerName;

    public Integer getCustomerId() {
        return customerId;
    }

    public void setCustomerId(Integer customerId) {
        this.customerId = customerId;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(o == null || getClass() != o.getClass()) {
            return false;
        }

        Customer customer = (Customer) o;

        if(customerId != null ? !customerId.equals(customer.customerId) : customer.customerId != null) {
            return false;
        }
        if(customerName != null ? !customerName.equals(customer.customerName) : customer.customerName != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = customerId != null ? customerId.hashCode() : 0;
        result = 31 * result + (customerName != null ? customerName.hashCode() : 0);
        return result;
    }
}
Customer.java
package com.nucsoft.hibernate;

/**
 * @author solverpeng
 * @create 2016-10-11-13:23
 */
public class Order {
    private Integer orderId;
    private String orderName;

    public Integer getOrderId() {
        return orderId;
    }

    public void setOrderId(Integer orderId) {
        this.orderId = orderId;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(o == null || getClass() != o.getClass()) {
            return false;
        }

        Order order = (Order) o;

        if(orderId != null ? !orderId.equals(order.orderId) : order.orderId != null) {
            return false;
        }
        if(orderName != null ? !orderName.equals(order.orderName) : order.orderName != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = orderId != null ? orderId.hashCode() : 0;
        result = 31 * result + (orderName != null ? orderName.hashCode() : 0);
        return result;
    }
}
Order.java
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="com.nucsoft.hibernate.Customer" table="customer" schema="hibernate">
        <id name="customerId">
            <column name="customer_id" sql-type="int(11)"/>
        </id>
        <property name="customerName">
            <column name="customer_name" sql-type="varchar(50)" length="50" not-null="true"/>
        </property>
    </class>
</hibernate-mapping>
Customer.hbm.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="com.nucsoft.hibernate.Order" table="order" schema="hibernate">
        <id name="orderId">
            <column name="order_id" sql-type="int(11)"/>
        </id>
        <property name="orderName">
            <column name="order_name" sql-type="varchar(50)" length="50" not-null="true"/>
        </property>
    </class>
</hibernate-mapping>
Order.hbm.xml

<2>手動建立關聯關係

①在 Order 一端建立多對一的關聯關係。

  • 在 Order 持久化類中添加 Customer 類型的一個屬性 customer。
  • 在 Order.hbm.xml 文件中添加多對一的關聯關係。同時修改主鍵生成方式為 native。

②在 Customer 一端建立一對多的關聯關係。

  • 在 Customer 持久化類中添加 Order 的一個集合 orders。
  • 在 Customer.hbm.xml 添加一對多的關聯關係。同時修改主鍵生成方式為 native。

③詳細說明:在 Customer.hbm.xml 文件中添加一對多的關聯關係。

  • 當 Session 從資料庫中載入 Java 集合時,創建的是 Hibernate 內置的集合類的實例。因此在持久化類中定義集合屬性時需要定義成介面類型,不能是具體的某個實現類。
    • Hibernate 內置的集合具有集合代理功能,因為有代理功能,所以支持延遲檢索策略。  
  • 在定義集合的時候,通常將其初始化為集合實現類的一個實例,防止 NullPointerException。
  • Hibernate 使用 <set> 元素來映射 Set 類型的屬性。
  • 1 的一端的 Set 類型屬性數據還是存放在 n 的一端。

④ set 元素

  • name 屬性:待映射的 Set 類型的屬性的屬性名稱。
  • table 屬性:待映射的 Set 屬性的泛型類型所對應的表。
  • key 子元素:column 屬性,多的一端的外鍵名稱。
  • one-to-many 子元素:class 屬性,n 的一端的持久化類名稱。

對應關係如圖。

⑤最終的實體類和 Entity.hbm.xml 文件。

package com.nucsoft.hibernate;

import java.util.HashSet;
import java.util.Set;

/**
 * @author solverpeng
 * @create 2016-10-11-13:23
 */
public class Customer {
    private Integer customerId;
    private String customerName;

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

    public Set<Order> getOrders() {
        return orders;
    }

    public void setOrders(Set<Order> orders) {
        this.orders = orders;
    }

    public Integer getCustomerId() {
        return customerId;
    }

    public void setCustomerId(Integer customerId) {
        this.customerId = customerId;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(o == null || getClass() != o.getClass()) {
            return false;
        }

        Customer customer = (Customer) o;

        if(customerId != null ? !customerId.equals(customer.customerId) : customer.customerId != null) {
            return false;
        }
        if(customerName != null ? !customerName.equals(customer.customerName) : customer.customerName != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = customerId != null ? customerId.hashCode() : 0;
        result = 31 * result + (customerName != null ? customerName.hashCode() : 0);
        return result;
    }
}
Customer.java
package com.nucsoft.hibernate;

/**
 * @author solverpeng
 * @create 2016-10-11-13:23
 */
public class Order {
    private Integer orderId;
    private String orderName;

    private Customer customer;

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public Integer getOrderId() {
        return orderId;
    }

    public void setOrderId(Integer orderId) {
        this.orderId = orderId;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(o == null || getClass() != o.getClass()) {
            return false;
        }

        Order order = (Order) o;

        if(orderId != null ? !orderId.equals(order.orderId) : order.orderId != null) {
            return false;
        }
        if(orderName != null ? !orderName.equals(order.orderName) : order.orderName != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = orderId != null ? orderId.hashCode() : 0;
        result = 31 * result + (orderName != null ? orderName.hashCode() : 0);
        return result;
    }
}
Order.java
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.nucsoft.hibernate">

    <class name="Customer" table="customer" schema="hibernate">
        <id name="customerId">
            <column name="customer_id" sql-type="int(11)"/>
            <generator class="native"/>
        </id>
        <property name="customerName">
            <column name="customer_name" sql-type="varchar(50)" length="50" not-null="true"/>
        </property>
        <set name="orders" table="order">
            <key column="customer_id"/>
            <one-to-many class="Order"/>
        </set>
    </class>
</hibernate-mapping>
Customer.hbm.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.nucsoft.hibernate">

    <class name="Order" table="order" schema="hibernate">
        <id name="orderId">
            <column name="order_id" sql-type="int(11)"/>
            <generator class="native"/>
        </id>
        <property name="orderName">
            <column name="order_name" sql-type="varchar(50)" length="50" not-null="true"/>
        </property>
        <many-to-one name="customer" class="Customer" column="customer_id"/>
    </class>
</hibernate-mapping>
Order.hbm.xml

(4)通過 Intellij Idea 直接生成雙向的多對一的關聯關係。

<1>為生成的每個 Entity.hbm.xml 文件添加主鍵生成方式。

<2>為 Customer 類中的 orders 屬性進行初始化。

<3>最終的持久化類和 Entity.hbm.xml。

package com.nucsoft.hibernate;

import java.util.HashSet;
import java.util.Set;

/**
 * @author solverpeng
 * @create 2016-10-11-14:01
 */
public class Customer {
    private Integer customerId;
    private String customerName;
    private Set<Order> orders = new HashSet<>();

    public Integer getCustomerId() {
        return customerId;
    }

    public void setCustomerId(Integer customerId) {
        this.customerId = customerId;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) {
            return true;
        }
        if(o == null || getClass() != o.getClass()) {
            return false;
        }

        Customer customer = (Customer) o;

        	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在使用R的時候會發現R對CPU的利用率並不是很高,反正當我在使用R的時候,無論R做何種運算R的CPU利用率都只有百分子幾,這就導致一旦計算量大的時候計算時間非常長,會給人一種錯覺(R真的在計算嗎?會不會我的程式死掉了?)。今天,我看到了一篇博客介紹的方法,迫不及待的嘗試了一下,只能說:太牛逼了!下麵 ...
  • 當滑鼠點擊和放開時發生動作事件; 方法摘要: actionPerformed(ActionEvent e) 發生操作時調用。 列: public class shijian extends Frame{ public static void main(String[] args) { Frame f ...
  • 問題:從 XE4 以來,Firemonkey 曲線繪圖在移動平臺不平滑的問題一直令人詬病,提交到官方的 QC 也是族繁不及備載,官方似乎有意的避開這個問題,遲遲沒有修正。 適用版本:XE4 ~ Berlin 10.1 update 1 (查過官方源碼從 XE4 開始有 FMX.StrokeBuild ...
  • re 正則表達式操作 本模塊提供了類似於Perl的正則表達式匹配操作。要匹配的模式和字元串可以是Unicode字元串以及8位字元串。 正則表達式使用反斜杠字元('\')來表示特殊的形式或者來允許使用特殊的字元而不要啟用它們特殊的含義。這與字元串字面值中相同目的的相同字元的用法衝突;例如,要匹配一個反 ...
  • 作為一個學習java的人,首先我們要瞭解java是幹嘛的,java能做什麼,以及java的歷史背景,只有當我們瞭解了java這些基礎,我們學習java才能事倍功半。 Java的歷史背景 Java是由Sun Microsystems公司推出的Java面向對象程式設計語言(以下簡稱Java語言)和Jav ...
  • 一個C程式是由一個或者多個函數組成,並必須只有一個main()函數。我建議不要使用void main的形式,同建議用 int main 然後return 0;的形式,至於為什麼要這樣做,我也不知道。 一個文件內、一個函數內、一個控制語句內、一對花括弧內,都可以稱為一個代碼塊。 聲明與定義 int n ...
  • 題目鏈接 http://acm.hdu.edu.cn/showproblem.php?pid=5091 Problem Description Recently, the γ galaxies broke out Star Wars. Each planet is warring for resou ...
  • 一、看一下簡單的通過XML的AOP配置 1.首先創建一個簡單的Student類 2.創建一個簡單的aspect切麵class 3.SpringAOP.xml配置 分析一下這個execution(* com.seeyon.SpringBean.aop.Student.get*(..))切點表達式: ( ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...