步步为营Hibernate全攻略(四)剪不断理还乱之:复合主键 && 组合映射
一:复合主键
复合主键即两个或多个字段联合起来作为主键,它的通常做法是将主键相关字段抽取出来放到一个单独的类中,但是这样的类是有要求的:
1. 必须实现序列化接口
2. 必须覆盖equals和hashCode方法
以会计核算期中核算年和核算月做主键为例,将这两个主键相关字段放到FiscalYearPeriodPK类中,代码如下:
package com.bjpowernode.hibernate; import java.io.Serializable; public class FiscalYearPeriodPK implements Serializable { //核算年 private int fiscalYear; //核算月 private int fiscalPeriod; public int getFiscalYear() { return fiscalYear; } public void setFiscalYear(int fiscalYear) { this.fiscalYear = fiscalYear; } public int getFiscalPeriod() { return fiscalPeriod; } public void setFiscalPeriod(int fiscalPeriod) { this.fiscalPeriod = fiscalPeriod; } //要实现序列化接口需要覆盖hashCode和equals方法,这样才能比较 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + fiscalPeriod; result = prime * result + fiscalYear; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; FiscalYearPeriodPK other = (FiscalYearPeriodPK) obj; if (fiscalPeriod != other.fiscalPeriod) return false; if (fiscalYear != other.fiscalYear) return false; return true; } }
持有该类引用的FiscalYearPeriod类代码如下:
package com.bjpowernode.hibernate; import java.util.Date; public class FiscalYearPeriod { private FiscalYearPeriodPK fiscalYearPeriodPK; //开始日期 private Date beginDate; //结束日期 private Date endDate; //状态 private String periodSts; public Date getBeginDate() { return beginDate; } public void setBeginDate(Date beginDate) { this.beginDate = beginDate; } public Date getEndDate() { return endDate; } public void setEndDate(Date endDate) { this.endDate = endDate; } public String getPeriodSts() { return periodSts; } public void setPeriodSts(String periodSts) { this.periodSts = periodSts; } public FiscalYearPeriodPK getFiscalYearPeriodPK() { return fiscalYearPeriodPK; } public void setFiscalYearPeriodPK(FiscalYearPeriodPK fiscalYearPeriodPK) { this.fiscalYearPeriodPK = fiscalYearPeriodPK; } }
来看最重要的部分,映射文件是如何进行配置的,在复合主键映射的配置中需要引入一个新的标签<composite-id>,这个标签的作用就是告诉Hibernate,生成的表使用的是复合主键,其中的<key>标签便是具体的联合主键字段,下面是映射文件内容:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.bjpowernode.hibernate.FiscalYearPeriod" table="t_fiscal_year_period"> <composite-id name="fiscalYearPeriodPK"> <key-property name="fiscalYear"/> <key-property name="fiscalPeriod"/> </composite-id> <property name="beginDate" type="date"/> <property name="endDate" type="date"/> <property name="periodSts"/> </class> </hibernate-mapping>
二:组合映射
采用组合映射的条件是由两个或多个类,他们持有多个相同的具有相同特点的成员变量(变量类型和名称一样),这时可以将这些相同的成员变量抽取出来放到另外一个类中,该类只是实体的一部分(注意这个类并不是实体类),与实体类相比它没有对象id,我们将其称之为值类。比如有User和Employee两个类,均持有 ,id,name,email,address,zipCode,contactTel属性,通过观察可知,后四个字段都属于通讯方面的信息,所以可以将这四个字段抽取出来放到类Contact中,然后让User和Employee类都持有Contact的引用即可,只要在映射文件中做好相应的配置,同样可以达到原来的映射效果。下面来看User.hbm.xml(Employee.hbm.xml与User.hbm.xml内容类似)映射文件是如何配置的:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- User.hbm.xml文件 --> <class name="com.bjpowernode.hibernate.User" table="t_user"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <component name="userContact"> <property name="email"/> <property name="address"/> <property name="zipCode"/> <property name="contactTel"/> </component> </class> <!-- Employee.hbm.xml文件 --> <class name="com.bjpowernode.hibernate.Employee" table="t_employee"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <component name="employeeContact"> <property name="email"/> <property name="address"/> <property name="zipCode"/> <property name="contactTel"/> </component> </class> </hibernate-mapping>
通过上述配置同样可以达到每个类持有六个成员变量的效果,但是使用组合映射可以实现对象模型的细粒度划分,复用率高,含义明确,另外如果想要扩展属性的只要在抽取的类中进行扩展就可以,更加灵活。