学到Hibernate的多对一映射,书上列举的例子是多个订单(Order)对应一个客户(Customer)对应,同时提到在Hibernate中通过比较两个持久化对象的标识符属性值(ID)来判断二者是否相等,这就需要重写实体类的equals()方法和hashCode()方法。
在网上查了一下资料,然后自己调试了一下,颇有收获,整理如下:
equals()和hashCode()这两个方法属于Object类,而Object类是所有类的父类,因此所有的类都继承了这两个方法。其中有一些类重写了这两个方法。
例如:Object类的equals()方法代码如下:
- public boolean equals(Object obj) {
- return (this == obj);
- }
而String类的equals()方法代码如下:
- public boolean equals(Object anObject) {
- if (this == anObject) {
- return true;
- }
- if (anObject instanceof String) {
- String anotherString = (String)anObject;
- int n = count;
- if (n == anotherString.count) {
- char v1[] = value;
- char v2[] = anotherString.value;
- int i = offset;
- int j = anotherString.offset;
- while (n-- != 0) {
- if (v1[i++] != v2[j++])
- return false;
- }
- return true;
- }
- }
- return false;
- }
可以看到,Object类的equals()方法的判断依据是引用地址,而String类的equals()方法则是在此基础上加了另一个IF判断,判断两个字符串里的字符是否对应相等。即如果两个字符串变量的引用地址不同,但是是同一个字符串的话,这两个字符串被视为相等。
因此通过重写equals()方法可以按照自己的意愿指定判定两个对象是否相等的依据。
hashCode()方法的作用是返回指定对象的哈希值。
在集合类(HashMap,HashSet等)中判断两个对象是否相等有如下规则:
如果两个对象哈希值不同,那么这两个对象不相等。如果相同,则调用equals()方法判断,如果equals()方法返回true,则这两个对象相等,否则不相等。
当equals方法返回值为true时,两个对象的hashCode值必定相等。
当equals方法返回值为false时,两个对象的hashCode值不一定不同。
如果重写了equals()方法,就必须重写hashCode()方法。
具体的原因可以参照这篇文章:
http://blog.csdn.net/michaellufhl/article/details/5833188
讲解的非常透彻,不在此赘述了。
下面解释在Hibernate中为什么要在实体类中重写equals()方法和hashCode()方法
在多对一映射中用HashSet来存放订单对象(Order)
两张数据库表如下:
两个实体类:
Customer类如下:
- package pojo;
- import java.io.Serializable;
- import java.util.HashSet;
- import java.util.Set;
- public class Customer implements Serializable{
- private Integer id;
- private String userName;
- private String password;
- private String realName;
- private String address;
- private String mobile;
- private Set<Order> orders=new HashSet<Order>(0);
- public Set<Order> getOrders() {
- return orders;
- }
- public void setOrders(Set<Order> orders) {
- this.orders = orders;
- }
- public Customer(String userName,String password,String realName,
- String address,String mobile)
- {
- this.userName=userName;
- this.password=password;
- this.realName=realName;
- this.address=address;
- this.mobile=mobile;
- }
- public Customer()
- {
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getRealName() {
- return realName;
- }
- public void setRealName(String realName) {
- this.realName = realName;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- public String getMobile() {
- return mobile;
- }
- public void setMobile(String mobile) {
- this.mobile = mobile;
- }
- //重写hashCode方法
- @Override
- public int hashCode()
- {
- final int prime=31;
- int result=1;
- result=prime * result+((id==null)?0:id.hashCode());
- return result;
- }
- //重写equals方法
- @Override
- public boolean equals(Object obj)
- {
- if(this==obj)
- {
- return true;
- }
- if(obj==null)
- {
- return false;
- }
- if(getClass()!=obj.getClass())
- {
- return false;
- }
- Customer other=(Customer)obj;
- if(id==null)
- {
- if(other.id!=null)
- {
- return false;
- }
- }
- else if(!id.equals(other.id))
- {
- return false;
- }
- return true;
- }
- }
Order类如下
可以看到,Order中重写的hashCode()方法中,哈希值和Order的id属性相关,两个ID值不同的Order对象的哈希值相同的几率很小。
- package pojo;
- import java.io.Serializable;
- import java.util.Date;
- public class Order implements Serializable{
- private Integer id;
- private String orderNo;
- private Date date;
- private double total;
- private Customer customer;
- public Order()
- {
- }
- public Order(String orderNo,Date date,double total,Customer customer)
- {
- this.orderNo=orderNo;
- this.date=date;
- this.total=total;
- this.customer=customer;
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getOrderNo() {
- return orderNo;
- }
- public void setOrderNo(String orderNo) {
- this.orderNo = orderNo;
- }
- public Date getDate() {
- return date;
- }
- public void setDate(Date date) {
- this.date = date;
- }
- public double getTotal() {
- return total;
- }
- public void setTotal(double total) {
- this.total = total;
- }
- public Customer getCustomer() {
- return customer;
- }
- public void setCustomer(Customer customer) {
- this.customer = customer;
- }
- //重写hashCode方法
- @Override
- public int hashCode()
- {
- final int prime=31;
- int result=1;
- result=prime * result+((id==null)?0:id.hashCode());
- return result;
- }
- @Override
- //重写equals方法
- public boolean equals(Object obj)
- {
- if(this==obj)
- {
- return true;
- }
- if(obj==null)
- {
- return false;
- }
- if(getClass()!=obj.getClass())
- {
- return false;
- }
- Order other=(Order)obj;
- if(id==null)
- {
- if(other.id!=null)
- {
- return false;
- }
- }
- else if(!id.equals(other.id))
- {
- return false;
- }
- return true;
- }
- }
Order类重写的equals()方法中,如果两个对象的ID值不同,那么这两个对象不相等。
即ID这个属性是区分同一类对象的标识符。
测试类如下:
- package test;
- import java.util.Set;
- import org.hibernate.Session;
- import pojo.Customer;
- import pojo.Order;
- import util.HibernateUtils;
- public class BusinessService2 {
- public static void main(String[] args) {
- findOrderOfCustomer(1);
- }
- public static void findOrderOfCustomer(Integer id)
- {
- Session session=HibernateUtils.getSession();
- //获取指定ID值的customer对象
- Customer customer=(Customer)session.get(Customer.class, id);
- //获取订单集合
- Set<Order> orders=customer.getOrders();
- System.out.println("客户"+customer.getUserName()+"订单如下");
- for(Order order:orders)
- {
- System.out.print("编号:"+order.getOrderNo()+",");
- System.out.print("总金额:"+order.getTotal()+",");
- System.out.print("下单日期:"+order.getDate()+",");
- System.out.println();
- }
- }
- }
测试输出结果如图:
如果将Order类的equals方法中的判断两个对象的ID属性是否相等改为判断Total属性是否相等
即:
- public boolean equals(Object obj)
- {
- if(this==obj)
- {
- return true;
- }
- if(obj==null)
- {
- return false;
- }
- if(getClass()!=obj.getClass())
- {
- return false;
- }
- Order other=(Order)obj;
- if(id==null)
- {
- if(other.id!=null)
- {
- return false;
- }
- }
- //判断Total属性是否相等
- else if(!(getTotal()==(other.getTotal())))
- {
- return false;
- }
- return true;
- }
再将重写的hashCode方法的返回值和total属性值关联,即
- public int hashCode()
- {
- final int prime=31;
- int result=1;
- //result=prime * result+((id==null)?0:id.hashCode());
- //使哈希值与total属性值关联
- result=(int)total*prime;
- return result;
- }
最后再将Order表中两条记录的TOTAL属性值都改为100
测试类运行结果如下:
可以看到控制台只输出了一条记录,因为刚才将两条记录的Total值都修改为100,同时将重写的hashCode的返回值改为与Total属性相关,所以两个对象的hashCode值相同,此时调用equals方法,而equals方法也修改为判断两个对象的Total值是否相同,所以此时两个对象被判定为相等,而HashSet不能放入相等的对象,所以只存放了一个Order对象。
数据库中的同一条记录可能对应多个引用值不同的持久化对象,如果不重写equals和hashCode方法,而是直接调用继承自Object类的两个方法,有可能同一条记录的多个对象由于地址不同而被Hibernate视为多条记录。
以上为个人整理,第一次写博客,纰漏在所难免,望海涵。