一、映射多對一關聯關係。 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;