上篇博文描述了Hibernate - 单向多对一关联关系映射,本篇博文继续学习单向一对多关系映射。

这里Customer:Order= 1:N,外键保存在Order表中。

【1】修改Customer和Order

Customer修改如下:

public class Customer {

private Integer customerId;
private String customerName;

private Set<Order> orders;

//拥有属性orders
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 String toString() {
return "Customer [customerId=" + customerId + ", customerName=" + customerName + ", orders=" + orders + "]";
}

}

Customer.hbm.xml修改如下:

<hibernate-mapping package="com.jane.model">

<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" default="null" />
</property>
<!--
name="orders"表示一的一端拥有多的那端的属性;
key节点中属性column="customer_id"表示外键(数据库多的一端拥有的)
-->
<set name="orders" table="ORDERS">
<key column="customer_id"></key>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>

​<key>​​元素设定本持久化类在Set对应表中的外键列名。如上值Customer类在ORDERS表中的外键列名–customer_id。

Order修改如下:

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 String toString() {
return "Order [orderId=" + orderId + ", orderName=" + orderName+"]";
}

}

Order.hbm.xml如下:

<hibernate-mapping package="com.jane.model">

<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" default="null" />
</property>
</class>
</hibernate-mapping>

生成的数据库表结构如下所示:

//Customers表
CREATE TABLE `customers` (
`CUSTOMER_ID` int(11) NOT NULL AUTO_INCREMENT,
`CUSTOMER_NAME` varchar(255) DEFAULT NULL,
PRIMARY KEY (`CUSTOMER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

//Orders表
CREATE TABLE `orders` (
`ORDER_ID` int(11) NOT NULL AUTO_INCREMENT,
`ORDER_NAME` varchar(255) DEFAULT NULL,
`customer_id` int(11) DEFAULT NULL,
PRIMARY KEY (`ORDER_ID`),
KEY `FK1nbewmmir6psft27yfvvmwpfg` (`customer_id`),
CONSTRAINT `FK1nbewmmir6psft27yfvvmwpfg` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`CUSTOMER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

可以发现,无论是多对一还是一对多,外键都是多的一端数据表所持有。

单向多对一中,以多的一端为主,在多的一端xml中使用​​<many-to-one/>​​​进行配置。
单向一对多中,以一的一端为主,在一的一端xml中使用​​​<one-to-many/>​​进行配置。


【2】代码测试

① 单向一对多持久化

和单向多对一持久化不同的是,单向 1-n 关联关系执行保存时, 一定会多出 UPDATE 语句。因为 n 的一端在插入时不会同时插入外键列。

@Test
public void testOneToManySave(){
Customer customer = new Customer();
customer.setCustomerName("AAA");
customer.setOrders(new HashSet<>());

Order order1 = new Order();
order1.setOrderName("O-JJ-1");

Order order2 = new Order();
order2.setOrderName("O-JJ-2");
System.out.println(customer);

//建立关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2);

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

}

测试结果如下:

Hibernate: 
insert
into
CUSTOMERS
(CUSTOMER_NAME)
values
(?)
Hibernate:
insert
into
ORDERS
(ORDER_NAME)
values
(?)
Hibernate:
insert
into
ORDERS
(ORDER_NAME)
values
(?)
Hibernate:
update
ORDERS
set
customer_id=?
where
ORDER_ID=?
Hibernate:
update
ORDERS
set
customer_id=?
where
ORDER_ID=?

② 单向一对多获取对象

默认对关联的多的一方使用懒加载的加载策略,只有在使用的时候才会进行查询。

测试代码如下:

@Test
public void testOneToManyGet(){
Customer customer = session.get(Customer.class, 1);
System.out.println(customer);

System.out.println(customer.getOrders().size());
}

测试结果如下:

Hibernate: 
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
from
CUSTOMERS customer0_
where
customer0_.CUSTOMER_ID=?
Hibernate:
select
orders0_.customer_id as customer3_2_0_,
orders0_.ORDER_ID as ORDER_ID1_2_0_,
orders0_.ORDER_ID as ORDER_ID1_2_1_,
orders0_.ORDER_NAME as ORDER_NA2_2_1_
from
ORDERS orders0_
where
orders0_.customer_id=?
Customer [customerId=1, customerName=AAA, orders=[Order [orderId=1, orderName=O-JJ-1], Order [orderId=2, orderName=O-JJ-2]]]
2

③ 单向一对多更新操作

代码如下:

@Test
public void testUpdate(){
Customer customer = session.get(Customer.class, 1);

customer.getOrders().iterator().next().setOrderName("O-XXX-10");
}

测试结果如下:

Hibernate: 
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
from
CUSTOMERS customer0_
where
customer0_.CUSTOMER_ID=?
Hibernate:
select
orders0_.customer_id as customer3_2_0_,
orders0_.ORDER_ID as ORDER_ID1_2_0_,
orders0_.ORDER_ID as ORDER_ID1_2_1_,
orders0_.ORDER_NAME as ORDER_NA2_2_1_
from
ORDERS orders0_
where
orders0_.customer_id=?
Hibernate:
update
ORDERS
set
ORDER_NAME=?
where
ORDER_ID=?

单独更新CustomerName如下:

@Test
public void testUpdate(){
Customer customer = session.get(Customer.class, 1);
customer.setCustomerName("CC");
System.out.println(customer.getCustomerName());
System.out.println(customer);
}

测试结果如下:

Hibernate: 
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
from
CUSTOMERS customer0_
where
customer0_.CUSTOMER_ID=?
CC
Hibernate:
select
orders0_.customer_id as customer3_2_0_,
orders0_.ORDER_ID as ORDER_ID1_2_0_,
orders0_.ORDER_ID as ORDER_ID1_2_1_,
orders0_.ORDER_NAME as ORDER_NA2_2_1_
from
ORDERS orders0_
where
orders0_.customer_id=?
Customer [customerId=1, customerName=CC, orders=[Order [orderId=1, orderName=O-XXX-10], Order [orderId=2, orderName=O-JJ-2]]]
Hibernate:
update
CUSTOMERS
set
CUSTOMER_NAME=?
where
CUSTOMER_ID=?

④ 单向一对多删除操作

默认情况下, 若删除 1 的一端, 则会先把关联的 n 的一端的外键置空, 然后进行删除。

测试代码如下:

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

测试结果如下:

//先查询
Hibernate:
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
from
CUSTOMERS customer0_
where
customer0_.CUSTOMER_ID=?
//更新Order表的外键Customer_ID 为null
Hibernate:
update
ORDERS
set
customer_id=null
where
customer_id=?
//删除Customers
Hibernate:
delete
from
CUSTOMERS
where
CUSTOMER_ID=?

综上可知,只有在持久化操作和删除操作时,单向一对多和单向多对一有所不同,在查找和更新时没有什么区别。

博文是基于XML进行讲解,那么注解版的如何使用呢?

参考博文:注解版的单向一对多