[转自] http://blog.csdn.net/happylee6688/article/details/17636801
最近做项目用到了Hibernate框架,采用了纯面向对象的思想,使用ORM映射实体。在开发中,实体中出现了复合主键,不再是单一的属性作主键,由于采用了注解的方式,就不再使用xml文件进行配置了,而是直接在实体中进行注释。
Hibernate注解规范的文档中提供了三种方法:
1. 将组件类注解为@Embeddable,并将组件的属性注解为@Id;
2. 将组件的属性注解为@Embeddable;
3. 将类注解为@IdClass,并将该实体中所有主键属性注解为@Id。
这里,我采用的是第三种方法——@IdClass,下面就是具体的代码,大家一块讨论一下。
首先,需要说明的是,采用@IdClass方式,需要根据所有的主键属性,建立一个主键类,该主键类包含所有的主键,而且,作为主键类,需要满足以下要求:
1. 主键类必须实现序列化接口(implements Serializable);
2. 主键类必须有默认的public无参数的构造方法;
3. 主键类必须覆盖equals和hashCode方法。
主键类IPMapKey(为了方便演示,这里都采用了String类型)
public class IPMapKey implements Serializable { /** * @Fields serialVersionUID :3176972128965536016L */ private static final long serialVersionUID = 3176972128965536016L; // 主键属性 private String ip; // 主键属性 private String examPlaceId; // 主键属性 private String examId; /** * 无参数的public构造方法,必须要有 */ public IPMapKey() { } /** * 重写了一个带参数的构造方法 * @param ip * @param examPlaceId * @param examId */ public IPMapKey(String ip, String examPlaceId, String examId) { this.ip = ip; this.examId = examId; this.examPlaceId = examPlaceId; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getExamPlaceId() { return examPlaceId; } public void setExamPlaceId(String examPlaceId) { this.examPlaceId = examPlaceId; } public String getExamId() { return examId; } public void setExamId(String examId) { this.examId = examId; } public static long getSerialversionuid() { return serialVersionUID; } /** * 覆盖hashCode方法,必须要有 */ @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + (ip == null ? 0 : ip.hashCode()); result = PRIME * result + (examId == null ? 0 : examId.hashCode()); result = PRIME * result + (examPlaceId ==null ? 0 : examPlaceId.hashCode()); return result; } /** * 覆盖equals方法,必须要有 */ @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(!(obj instanceof PaperKey)) return false; IPMapKey objKey = (IPMapKey)obj; if(ip.equalsIgnoreCase(objKey.ip) && examId.equalsIgnoreCase(objKey.examId) && examPlaceId.equalsIgnoreCase(objKey.examPlaceId)) { return true; } return false; } }
@Entity @Table(name="TE_IPMap") @IdClass(IPMapKey.class) public class IPMap { // 主键,这里需要添加@Id标记 @Id @Column(name="IP") private String ip; @Column(name="StudentNo") private String studentNo; // 主键,这里需要添加@Id标记 @Id @Column(name="ExamPlaceId") private String examPlaceId; // 主键,这里需要添加@Id标记 @Id @Column(name="ExamId", unique=true) private String examId; @Column(name="AddUser") private String addUser; @Column(name="TimeStamp") private String timeStamp; @Column(name="Remark") private String remark; public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getStudentNo() { return studentNo; } public void setStudentNo(String studentNo) { this.studentNo = studentNo; } public String getExamPlaceId() { return examPlaceId; } public void setExamPlaceId(String examPlaceId) { this.examPlaceId = examPlaceId; } public String getExamId() { return examId; } public void setExamId(String examId) { this.examId = examId; } public String getAddUser() { return addUser; } public void setAddUser(String addUser) { this.addUser = addUser; } public String getTimeStamp() { return timeStamp; } public void setTimeStamp(String timeStamp) { this.timeStamp = timeStamp; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } }
在主键类中,为了能使集合框架中的类(如HashMap)正常工作,必须同时覆盖equals和hashCode方法,而且不要由于写错参数类型,而重载了这个方法,却没有覆盖它们。
覆盖equals时总要覆盖hashCode,一个很常见的错误根源在没有覆盖hashCode方法。在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常工作,这样的集合包括HashMap、HashSet和Hashtable。
——摘自《Effective Java》
equals方法用于判断传入的对象是否相同,EntityManager通过find方法来查找Entity时,是根据equals方法的返回值来判断的。hashCode方法返回当前对象的哈希码,生成的hashCode相同的概率越小越好。