Hibernate中one-to-many保存many方

  使用Hibernate的人,对于one-to-many肯定不会陌生。最常见的做法是设置one方的inverse=”true”和cascade(xml方式。如果使用注解,那么就是mappedBy和cascade),然后在保存或更新时,仅对one方进行操作就可以级联到many方。
  但是有时候也会有一种需求,就是one方跟many方的数据变化不是同时的。比如说,一个顾客(Customer)可以有多个订单(Order),某个顾客可能先在系统中有了记录,然后过了一段时间,他下订单了。这个情景对应的两个类简单抽象如下: 

@Entity
@Table(name = "customer")
public class Customer {
    private int id;
    private String name;
    private Set<Order> orders = new HashSet<Order>();

    @Id
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}

    @Column(length = 50)
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

    @OneToMany(targetEntity = Order.class, cascade = CascadeType.ALL, mappedBy = "customer", orphanRemoval = true)
    public Set<Order> getOrders() {return orders;}
    public void setOrders(Set<Order> orders) {this.orders = orders;}

    public Customer(int id) {this.id = id;}
    public Customer() {}
}

@Entity
@Table(name = "orders")
public class Order {
    private int id;
    private String number;
    private Customer customer;

    @Id
    public int getId() {return id;}
    public void setId(int id) {this.id = id;}

    @Column(length = 50)
    public String getNumber() {return number;}
    public void setNumber(String number) {this.number = number;}

    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "customer_id")
    public Customer getCustomer() {return customer;}
    public void setCustomer(Customer customer) {this.customer = customer;}

    public Order(int id, String number, Customer customer)
    {
        this.id = id;
        this.number = number;
        this.customer = customer;
    }

    public Order() {}
}

  简化一下需求,假设这个顾客只下了一个订单,对应只有一个Order实例,名为order。这时候为了保存order,自然不能通过new一个Customer实例保存之,通过级联来实现保存order的方式,而应该直接保存order对象。那因为order有个customer成员变量,所以一个朴素的想法就是先通过session.get,得到Customer对象,然后setCustomer,再save。
  的确是很朴素的想法,但是我们已经知道了customer的id(废话,要不然怎么session.get),却仅仅是为了order.setCustomer(),就得去查询customer表格。如果数据量很大,会很耗时。这明显是很违反直觉的,JDBC中也不是这么操作。
  下面简单给出一个小示例,给出我的解决方式:

Session session = null;
Transaction transaction = null;
try
{
    session = HibernateUtil.createSession();
    transaction = session.beginTransaction();
    //已经知道了customer的id,则直接new一个Customer对象,只有id属性非空
    Order order1 = new Order(1, "orderNumber1", new Customer(1));
    //直接保存Order对象
    session.save(order1);
    transaction.commit();
}catch (Exception e)
{
    e.printStackTrace();
    if(null != transaction)
        transaction.rollback();
}finally
{
    if(null != session)
        HibernateUtil.close(session);
}

  上述代码,Hibernate产生的SQL语句如下:
Hibernate: select customer_.id, customer_.name as name2_0_ from customer customer_ where customer_.id=?
Hibernate: insert into orders (customer_id, number, id) values (?, ?, ?)
结果它还是去查找customer表了,我大处女座如何能忍??
  后面发现,为主键设置了generator之后,Hibernate就不会再去查询customer表了。如下:

@Entity
@Table(name = "customer")
public class Customer {
    private int id;
    private String name;
    private Set<Order> orders = new HashSet<Order>();

    @Id
    @GenericGenerator(name = "assigned", strategy = "assigned")
    @GeneratedValue(generator = "assigned")
    public int getId() {return id;}
    //...
}

@Entity
@Table(name = "orders")
public class Order {
    private int id;
    private String number;
    private Customer customer;

    @Id
    @GenericGenerator(name = "assigned", strategy = "assigned")
    @GeneratedValue(generator = "assigned")
    public int getId() {return id;}
    //...
}

最后产生的SQL语句如下:
Hibernate: insert into orders (customer_id, number, id) values (?, ?, ?)
嗯,舒坦了!!!!!

PS:前提已经知道customer的id。如果只知道name,那还是要去查询customer表,这是避不开的。

你可能感兴趣的:(Hibernate)