孙卫琴的《精通JPA与Hibernate》的读书笔记:映射一对多双向关联关系

当类与类之间建立了关联,就可以方便地从一个对象导航到另一个或者一组与它关联的对象。例如,对于给定的Order对象,如果想获得与它关联的Customer对象,只要调用如下方法:

//从Order对象导航到关联的Customer对象
Customer customer=order.getCustomer();

在Order类中,用@ManyToOne注解映射customer属性:

  @ManyToOne(targetEntity =Customer.class)
  @JoinColumn(name="CUSTOMER_ID")
  private Customer customer;

对于给定的客户,查询该客户的所有订单,只需要调用customer.getOrders()方法。

Hibernate要求在持久化类中定义集合类属性时,必须把属性声明为接口类型,如java.util.Set、java.util.Map和java.util.List。声明为接口可以提高持久化类的透明性,当Hibernate调用setOrders(Set orders)方法时,传递的参数是Hibernate自定义的实现该接口的类的实例。如果把orders声明为java.util.HashSet类型(它是java.util.Set接口的一个实现类),就强迫Hibernate只能把HashSet类的实例传给setOrders()方法。

在定义Customer类的orders集合属性时,通常把它初始化为集合实现类的一个实例,例如:

private Set<order> orders=new HashSet<order>();

这可以提高程序的健壮性,避免应用程序访问取值为null的orders集合的方法而抛出NullPointerException。例如,以下程序访问Customer对象的orders集合,即使orders集合中不包含任何元素,但是调用orders.iterator()方法也不会抛出NullPointerException异常,因为orders集合并不为null:

Set<Order> orders=customer.getOrders();
Iterator<Order> it=orders.iterator();
while(it.hasNext()){
  ……
}	

以下是Customer.java的源程序。

@Entity
@Table(name="CUSTOMERS")
public class Customer  implements java.io.Serializable {
  @Id
  @GeneratedValue(generator="increment")
  @GenericGenerator(name="increment", strategy = "increment")
  @Column(name="ID") 
  private Long id;

  @Column(name="NAME")
  private String name;

  @OneToMany(mappedBy="customer",
             targetEntity=Order.class,
             cascade=CascadeType.ALL)
  private Set<Order> orders = new HashSet<Order>();
  
  //此处省略构造方法,以及id和name属性的访问方法
  ……
  public Set<Order> getOrders(){
    return orders;
  }
  public void setOrders(Set<Order> orders) {
    this.orders=orders;
  }
}

对于Customer类的orders属性,由于在CUSTOMERS表中没有直接与orders属性对应的字段,因此不能用@Column注解来映射orders属性,而是要使用@OneToMany注解。@OneToMany注解包括以下属性。

  • targetEntity属性:指定orders集合中存放的是Order对象。
  • mappedBy属性:指定Order类中的customer属性引用所关联的Customer对象。
  • cascade:当取值为CascadeType.ALL,表示会执行级联保存、更新和删除等操作。

在双向关联关系中,可以把一方称为主动方,另一方称为被动方。主动方会负责维护关联关系,而被动方不负责维护关联关系。被动方用@OneToOne、@OneToMany和@ManyToMany注解来映射,并且设置了mappedBy属性。

在Customer类与Order类的一对多双向关联关系中,Customer类为“一”的一方,Order类为“多”的一方。Customer类作为“一”的一方,它的@OneToMany注解设置了mappedBy属性,因此Customer类是被动方,而Order类是主动方,负责维护两者之间的关联关系。
所谓维护关联关系,有两层含义:

  • (1)指在数据库中,主动方Order类对应的ORDERS表的外键参照CUSTOMERS表。假如Customer类的@OneToMany注解没有使用mappeBy属性,那么Customer类变成主动方,需要维护与Order类的关联关系,此时需要创建额外的CUSTOMER_ORDER连接表。
  • (2)Hibernate会根据主动方的持久化对象的关联关系的变化去同步更新数据库。
    孙卫琴的《精通JPA与Hibernate》的读书笔记:映射一对多双向关联关系
上一篇:孙卫琴的《精通JPA与Hibernate》的读书笔记:用orphanRemoval属性映射父子关系


下一篇:孙卫琴的《精通JPA与Hibernate》的读书笔记:映射对象标识符的基本原理