欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

04. 关联关系映射(一对多)

程序员文章站 2022-04-22 22:20:40
...

关联关系映射

一对多双向关联关系(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是倒转的意思。

  1. 默认inverse=false,子表的外键由父表来维护,会出现多条update语句,用父表的主键来更新子表的外键,这样确保子表的外键不会出现null,实际上子表的外键我们已经插入成功了,这种操作可以说是“多此一举”的,只是确保结果的正确性而已。如果我们设置inverse=true或者不设置,那么也可以插入成功。

  2. 当inverse=true时,子表的外键由子表来维护,不会出现后面的update操作,那么外键可能会出现null的现象。

  3. 注意:在多对多的情况下,inverse一定要设置为true,在任何一方都可以。

相关标签: 关联关系