04. 关联关系映射(一对多)
关联关系映射
一对多双向关联关系(Order和OrderLine)
我们使用订单(Order)和订单项(OrderLine)来演示这种关系
一对多双向关联关系的表结构
Order表
CREATE TABLE t_order(
id number(10) PRIMARY KEY,
ordered_date date NOT NULL,
shipped_date date,
total number(10,2)
)
OrderLine表
CREATE TABLE t_orderline(
id number(10) PRIMARY KEY,
price number(10,2),
quantity number(10),
product varchar2(30),
order_id number(10) REFERENCES t_order(id)
)
一对多双向关联关系的类结构
Order类
package com.li.association.one2many.pojo;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Order implements Serializable{
private static final long serialVersionUID = 1L;
private Long id;
private Date orderedDate;
private Date shippedDate;
private Double total;
private Set<OrderLine> orderLines=new HashSet<OrderLine>();
public Order() {}
public Order(Long id, Date orderedDate, Date shippedDate, Double total, Set<OrderLine> orderLines) {
super();
this.id = id;
this.orderedDate = orderedDate;
this.shippedDate = shippedDate;
this.total = total;
this.orderLines = orderLines;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getOrderedDate() {
return orderedDate;
}
public void setOrderedDate(Date orderedDate) {
this.orderedDate = orderedDate;
}
public Date getShippedDate() {
return shippedDate;
}
public void setShippedDate(Date shippedDate) {
this.shippedDate = shippedDate;
}
public Double getTotal() {
return total;
}
public void setTotal(Double total) {
this.total = total;
}
public Set<OrderLine> getOrderLines() {
return orderLines;
}
public void setOrderLines(Set<OrderLine> orderLines) {
this.orderLines = orderLines;
}
@Override
public String toString() {
return "Order [id=" + id + ", orderedDate=" + orderedDate + ", shippenDate=" + shippedDate + ", total=" + total
+ ", orderLines=" + orderLines + "]";
}
}
OrderLine类
package com.li.association.one2many.pojo;
public class OrderLine {
private Long id;
private Double price;
private Long quantity;
private String product;
private Order order;
public OrderLine() {}
public OrderLine(Long id, Double price, Long quantity, String product, Order order) {
super();
this.id = id;
this.price = price;
this.quantity = quantity;
this.product = product;
this.order = order;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Long getQuantity() {
return quantity;
}
public void setQuantity(Long quantity) {
this.quantity = quantity;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
@Override
public String toString() {
return "OrderLine [id=" + id + ", price=" + price + ", quantity=" + quantity + ", product=" + product + "]";
}
}
一对多双向关联关系的映射文件
order表(父表)
因为我们使用的是set集合来存储订单项(OrderLine),所以,我们要使用set标签来配置对应关系,如果我们使用List集合或者Map映射集来存储订单项,那么我们要用list标签或者map标签来配置,
name=”orderlines”:指示Order类中的属性(property),该属性用以设置或获取子表内容。
column=”ORDER_ID”:指示我们子表中的外键列。
<class name="Order" table="T_ORDER">
......
<set name="orderlines" cascade="all">
<key column="ORDER_ID"/>
<one-to-many class="OrderLine"/>
</set>
</class>
OrderLine表(子表)
name=”order”:指示OrderLine类中该有的属性。
culumn=”ORDER_ID”:对应外键。
<class name="OrderLine" table="T_ORDERLINE">
......
<many-to-one name="order" culumn="ORDER_ID"/>
</class>
测试
测试前我们应该要把映射文件配置进入核心配置文件中(hibernate.cfg.xml)
package com.li.test;
import java.util.Calendar;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.li.association.one2many.pojo.Order;
import com.li.association.one2many.pojo.OrderLine;
import com.li.common.HibernateSessionFactory;
/*一对多双向关联关系*/
public class Association3 {
public static void main(String[] args) {
OrderLine ol1=new OrderLine();
ol1.setId(456L);
ol1.setPrice(123.5);
ol1.setProduct("book");
ol1.setQuantity(3L);
OrderLine ol2=new OrderLine();
ol2.setId(789L);
ol2.setPrice(123.5);
ol2.setProduct("milk");
ol2.setQuantity(3L);
Order order=new Order();
order.setId(123L);
order.setOrderedDate(new Date(System.currentTimeMillis()));
Calendar cl=Calendar.getInstance();
cl.set(Calendar.DAY_OF_MONTH, cl.get(Calendar.DAY_OF_MONTH)+3);
order.setShippedDate(new Date(cl.getTimeInMillis()));
/*建立双向关联关系*/
order.getOrderLines().add(ol1);
order.getOrderLines().add(ol2);
ol1.setOrder(order);
ol2.setOrder(order);
order.setTotal(ol1.getPrice()*ol1.getQuantity()+ol2.getPrice()*ol2.getQuantity());
Session session = HibernateSessionFactory.getSession();
Transaction trans=null;
try {
trans = session.beginTransaction();
/*保存订单的同时会级联保存订单项*/
session.save(order);
/*级联查询*/
Order o=session.get(Order.class, 123L);
System.out.println(o);
/*级联更新*/
double total=0.0;
for(OrderLine ol : o.getOrderLines()) {
ol.setPrice(100.0);
ol.setQuantity(10L);
total+=ol.getPrice()*ol.getQuantity();
}
o.setTotal(total);
session.update(o);
/*级联删除*/
session.delete(o);
trans.commit();
} catch (Exception e) {
e.printStackTrace();
trans.rollback();
}
}
}
运行结果
Hibernate: select orderline_.ID, orderline_.PRICE as PRICE2_2_, orderline_.QUANTITY as QUANTITY3_2_, orderline_.PRODUCT as PRODUCT4_2_, orderline_.ORDER_ID as ORDER_ID5_2_ from T_ORDERLINE orderline_ where orderline_.ID=?
Hibernate: select orderline_.ID, orderline_.PRICE as PRICE2_2_, orderline_.QUANTITY as QUANTITY3_2_, orderline_.PRODUCT as PRODUCT4_2_, orderline_.ORDER_ID as ORDER_ID5_2_ from T_ORDERLINE orderline_ where orderline_.ID=?
Hibernate: insert into T_ORDER (ORDERED_DATE, SHIPPED_DATE, TOTAL, ID) values (?, ?, ?, ?)
Hibernate: insert into T_ORDERLINE (PRICE, QUANTITY, PRODUCT, ORDER_ID, ID) values (?, ?, ?, ?, ?)
Hibernate: insert into T_ORDERLINE (PRICE, QUANTITY, PRODUCT, ORDER_ID, ID) values (?, ?, ?, ?, ?)
Hibernate: update T_ORDERLINE set ORDER_ID=? where ID=?
Hibernate: update T_ORDERLINE set ORDER_ID=? where ID=?
我们通过输出结果可以看到,Hibernate最后还执行了两条 update 语句,这是为什么呢?我们稍后解释,如果我们给set标签添加 inverse 属并设置值为 false,就不会执行后面的更新操作。
即:
<class name="Order" table="T_ORDER">
......
<set name="orderlines" cascade="all" inverse="true">
<key column="ORDER_ID"/>
<one-to-many class="OrderLine"/>
</set>
</class>
<set>元素中的inverse元素
inverse是倒转的意思。
默认inverse=false,子表的外键由父表来维护,会出现多条update语句,用父表的主键来更新子表的外键,这样确保子表的外键不会出现null,实际上子表的外键我们已经插入成功了,这种操作可以说是“多此一举”的,只是确保结果的正确性而已。如果我们设置inverse=true或者不设置,那么也可以插入成功。
当inverse=true时,子表的外键由子表来维护,不会出现后面的update操作,那么外键可能会出现null的现象。
注意:在多对多的情况下,inverse一定要设置为true,在任何一方都可以。