建立域模型和关系数据模型有着不同的出发点:
- 域模型是由程序代码组成的,通过细化持久化类的粒度可提高代码可重用性,简化编程。
- 关系数据模型是由关系数据组成的。在存在数据冗余的情况下,需要把粗粒度的表拆分成具有外键参照关系的几个细粒度的表,从而节省存储空间;另一方面,在没有数据冗余的前提下,应该尽可能减少表的数目,简化表之间的参照关系,以便提高访问数据库的速度。因此,在建立关系数据模型时,需要在节省数据存储空间和节省数据操纵时间这两者之间进行折中。
由于建立域模型和关系数据模型的原则不一样,使得持久化类的数目往往比数据库表的数目多,而且持久化类的属性并不和表的字段一一对应。在下图中,Customer类的homeAddress属性及comAddress属性均和CUSTOMERS表中的多个字段对应。
Adress类不是独立的实体类,不能用@Entity注解来映射,而是用来自于JPA的@Embeddable注解来映射:
@Embeddable
public class Address implements java.io.Serializable {
@Column(name="HOME_PROVINCE")
private String province;
@Column(name="HOME_CITY")
private String city;
@Column(name="HOME_STREET")
private String street;
@Column(name="HOME_ZIPCODE")
private String zipcode;
@Parent
private Customer customer;
//此处省略构造方法,以及所有属性的访问方法
……
}
以上Address类还有一个customer属性,用来自于org.hibernate.annotations包的@Parent注解来映射Address类的customer属性。
在Customer类中,使用来自于JPA的@Embedded注解来映射homeAddress属性和comAddress属性:
@Embedded
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(
name = "province",
column = @Column(name = "COM_PROVINCE")
),
@AttributeOverride(
name = "city",
column = @Column(name = "COM_CITY")
),
@AttributeOverride(
name = "street",
column = @Column(name = "COM_STREET")
),
@AttributeOverride(
name = "zipcode",
column = @Column(name = "COM_ZIPCODE"))
})
private Address comAddress;
值得注意的是,以上@Embedded注解也可以省略。因为当Address类用@Embeddable注解来映射时,JPA会自动判断出Customer类的homeAddress属性和comAddress属性是嵌入式类型。
在Address类中,进行了以下映射:
- 通过@Column注解把province属性和CUSTOMERS表中的HOME_PROVINCE字段映射。
- 对于Customer类的homeAddress属性,它的homeAddress.province属性和CUSTOMERS表的HOME_PROVINCE字段对应;
- 对于Customer类的comAddress属性,它的comAddress.province属性和CUSTOMERS表的COM_PROVINCE字段对应。因此在映射Customer类的comAddress属性时,需要通过@AttributeOverrides注解来重新设定comAddress.province属性和CUSTOMERS表的COM_PROVINCE字段的对应关系。