Hibernate中为什么要重写equals()和hashCode()这两个方法

时间:2022-08-22 16:17:11

学到Hibernate的多对一映射,书上列举的例子是多个订单(Order)对应一个客户(Customer)对应,同时提到在Hibernate中通过比较两个持久化对象的标识符属性值(ID)来判断二者是否相等,这就需要重写实体类的equals()方法和hashCode()方法。


在网上查了一下资料,然后自己调试了一下,颇有收获,整理如下:

equals()和hashCode()这两个方法属于Object类,而Object类是所有类的父类,因此所有的类都继承了这两个方法。其中有一些类重写了这两个方法。


例如:Object类的equals()方法代码如下:

[java]  view plain copy
  1. public boolean equals(Object obj) {  
  2.     return (this == obj);  
  3.     }  

而String类的equals()方法代码如下:

[java]  view plain copy
  1. public boolean equals(Object anObject) {  
  2.     if (this == anObject) {  
  3.         return true;  
  4.     }  
  5.     if (anObject instanceof String) {  
  6.         String anotherString = (String)anObject;  
  7.         int n = count;  
  8.         if (n == anotherString.count) {  
  9.         char v1[] = value;  
  10.         char v2[] = anotherString.value;  
  11.         int i = offset;  
  12.         int j = anotherString.offset;  
  13.         while (n-- != 0) {  
  14.             if (v1[i++] != v2[j++])  
  15.             return false;  
  16.         }  
  17.         return true;  
  18.         }  
  19.     }  
  20.     return false;  
  21.     }  

可以看到,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)


两张数据库表如下:

Hibernate中为什么要重写equals()和hashCode()这两个方法Hibernate中为什么要重写equals()和hashCode()这两个方法

两个实体类:


Customer类如下:

[java]  view plain copy
  1. package pojo;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.HashSet;  
  5. import java.util.Set;  
  6.   
  7. public class Customer implements Serializable{  
  8.     private Integer id;  
  9.     private String userName;  
  10.     private String password;  
  11.     private String realName;  
  12.     private String address;  
  13.     private String mobile;  
  14.       
  15.     private Set<Order> orders=new HashSet<Order>(0);  
  16.       
  17.     public Set<Order> getOrders() {  
  18.         return orders;  
  19.     }  
  20.     public void setOrders(Set<Order> orders) {  
  21.         this.orders = orders;  
  22.     }  
  23.     public Customer(String userName,String password,String realName,  
  24.             String address,String mobile)  
  25.     {  
  26.         this.userName=userName;  
  27.         this.password=password;  
  28.         this.realName=realName;  
  29.         this.address=address;  
  30.         this.mobile=mobile;  
  31.     }  
  32.     public Customer()  
  33.     {  
  34.           
  35.     }  
  36.     public Integer getId() {  
  37.         return id;  
  38.     }  
  39.     public void setId(Integer id) {  
  40.         this.id = id;  
  41.     }  
  42.     public String getUserName() {  
  43.         return userName;  
  44.     }  
  45.     public void setUserName(String userName) {  
  46.         this.userName = userName;  
  47.     }  
  48.     public String getPassword() {  
  49.         return password;  
  50.     }  
  51.     public void setPassword(String password) {  
  52.         this.password = password;  
  53.     }  
  54.     public String getRealName() {  
  55.         return realName;  
  56.     }  
  57.     public void setRealName(String realName) {  
  58.         this.realName = realName;  
  59.     }  
  60.     public String getAddress() {  
  61.         return address;  
  62.     }  
  63.     public void setAddress(String address) {  
  64.         this.address = address;  
  65.     }  
  66.     public String getMobile() {  
  67.         return mobile;  
  68.     }  
  69.     public void setMobile(String mobile) {  
  70.         this.mobile = mobile;  
  71.     }  
  72.     //重写hashCode方法  
  73.     @Override  
  74.     public int hashCode()  
  75.     {  
  76.         final int prime=31;  
  77.         int result=1;  
  78.         result=prime * result+((id==null)?0:id.hashCode());  
  79.         return result;  
  80.     }  
  81.     //重写equals方法  
  82.     @Override  
  83.     public boolean equals(Object obj)  
  84.     {         
  85.         if(this==obj)  
  86.         {  
  87.             return true;  
  88.         }  
  89.         if(obj==null)  
  90.         {  
  91.             return false;  
  92.         }  
  93.         if(getClass()!=obj.getClass())  
  94.         {  
  95.             return false;  
  96.         }  
  97.         Customer other=(Customer)obj;  
  98.         if(id==null)  
  99.         {  
  100.             if(other.id!=null)  
  101.             {  
  102.                 return false;  
  103.             }  
  104.         }  
  105.         else if(!id.equals(other.id))  
  106.         {  
  107.             return false;  
  108.         }  
  109.         return true;  
  110.     }  
  111. }  
[java]  view plain copy
  1.   
Order类如下

[java]  view plain copy
  1. package pojo;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.Date;  
  5.   
  6. public class Order implements Serializable{  
  7.     private Integer id;  
  8.     private String orderNo;  
  9.     private Date date;  
  10.     private double total;  
  11.     private Customer customer;  
  12.     public Order()  
  13.     {  
  14.           
  15.     }  
  16.     public Order(String orderNo,Date date,double total,Customer customer)  
  17.     {  
  18.         this.orderNo=orderNo;  
  19.         this.date=date;  
  20.         this.total=total;  
  21.         this.customer=customer;  
  22.     }  
  23.     public Integer getId() {  
  24.         return id;  
  25.     }  
  26.     public void setId(Integer id) {  
  27.         this.id = id;  
  28.     }  
  29.     public String getOrderNo() {  
  30.         return orderNo;  
  31.     }  
  32.     public void setOrderNo(String orderNo) {  
  33.         this.orderNo = orderNo;  
  34.     }  
  35.     public Date getDate() {  
  36.         return date;  
  37.     }  
  38.     public void setDate(Date date) {  
  39.         this.date = date;  
  40.     }  
  41.     public double getTotal() {  
  42.         return total;  
  43.     }  
  44.     public void setTotal(double total) {  
  45.         this.total = total;  
  46.     }  
  47.     public Customer getCustomer() {  
  48.         return customer;  
  49.     }  
  50.     public void setCustomer(Customer customer) {  
  51.         this.customer = customer;  
  52.     }  
  53.     //重写hashCode方法  
  54.     @Override  
  55.     public int hashCode()  
  56.     {         
  57.           
  58.         final int prime=31;  
  59.         int result=1;  
  60.         result=prime * result+((id==null)?0:id.hashCode());  
  61.         return result;  
  62.     }  
  63.     @Override  
  64.     //重写equals方法  
  65.     public boolean equals(Object obj)  
  66.     {         
  67.         if(this==obj)  
  68.         {  
  69.             return true;  
  70.         }  
  71.         if(obj==null)  
  72.         {  
  73.             return false;  
  74.         }  
  75.         if(getClass()!=obj.getClass())  
  76.         {  
  77.             return false;  
  78.         }  
  79.         Order other=(Order)obj;  
  80.         if(id==null)  
  81.         {  
  82.             if(other.id!=null)  
  83.             {  
  84.                 return false;  
  85.             }  
  86.         }  
  87.         else if(!id.equals(other.id))  
  88.         {  
  89.             return false;  
  90.         }  
  91.         return true;  
  92.     }  
  93. }  
可以看到,Order中重写的hashCode()方法中,哈希值和Order的id属性相关,两个ID值不同的Order对象的哈希值相同的几率很小。

Order类重写的equals()方法中,如果两个对象的ID值不同,那么这两个对象不相等。

即ID这个属性是区分同一类对象的标识符。


测试类如下:

[java]  view plain copy
  1. package test;  
  2.   
  3. import java.util.Set;  
  4.   
  5. import org.hibernate.Session;  
  6.   
  7. import pojo.Customer;  
  8. import pojo.Order;  
  9. import util.HibernateUtils;  
  10.   
  11. public class BusinessService2 {  
  12.     public static void main(String[] args) {  
  13.         findOrderOfCustomer(1);  
  14.     }  
  15.     public static void findOrderOfCustomer(Integer id)  
  16.     {  
  17.         Session session=HibernateUtils.getSession();  
  18.         //获取指定ID值的customer对象  
  19.         Customer customer=(Customer)session.get(Customer.class, id);  
  20.         //获取订单集合  
  21.         Set<Order> orders=customer.getOrders();         
  22.         System.out.println("客户"+customer.getUserName()+"订单如下");  
  23.         for(Order order:orders)  
  24.         {                 
  25.             System.out.print("编号:"+order.getOrderNo()+",");  
  26.             System.out.print("总金额:"+order.getTotal()+",");        
  27.             System.out.print("下单日期:"+order.getDate()+",");        
  28.             System.out.println();         
  29.         }  
  30.     }  
  31. }  


测试输出结果如图:

Hibernate中为什么要重写equals()和hashCode()这两个方法


如果将Order类的equals方法中的判断两个对象的ID属性是否相等改为判断Total属性是否相等

即:

[java]  view plain copy
  1. public boolean equals(Object obj)  
  2.     {         
  3.         if(this==obj)  
  4.         {  
  5.             return true;  
  6.         }  
  7.         if(obj==null)  
  8.         {  
  9.             return false;  
  10.         }  
  11.         if(getClass()!=obj.getClass())  
  12.         {  
  13.             return false;  
  14.         }  
  15.         Order other=(Order)obj;  
  16.         if(id==null)  
  17.         {  
  18.             if(other.id!=null)  
  19.             {  
  20.                 return false;  
  21.             }  
  22.         }  
  23.         //判断Total属性是否相等  
  24.         else if(!(getTotal()==(other.getTotal())))  
  25.         {  
  26.             return false;  
  27.         }  
  28.         return true;  
  29.     }  

再将重写的hashCode方法的返回值和total属性值关联,即


[java]  view plain copy
  1. public int hashCode()  
  2.     {         
  3.           
  4.         final int prime=31;  
  5.         int result=1;  
  6.         //result=prime * result+((id==null)?0:id.hashCode());  
  7.           
  8.         //使哈希值与total属性值关联  
  9.         result=(int)total*prime;  
  10.         return result;  
  11.     }  


最后再将Order表中两条记录的TOTAL属性值都改为100


测试类运行结果如下:


Hibernate中为什么要重写equals()和hashCode()这两个方法


可以看到控制台只输出了一条记录,因为刚才将两条记录的Total值都修改为100,同时将重写的hashCode的返回值改为与Total属性相关,所以两个对象的hashCode值相同,此时调用equals方法,而equals方法也修改为判断两个对象的Total值是否相同,所以此时两个对象被判定为相等,而HashSet不能放入相等的对象,所以只存放了一个Order对象。


数据库中的同一条记录可能对应多个引用值不同的持久化对象,如果不重写equals和hashCode方法,而是直接调用继承自Object类的两个方法,有可能同一条记录的多个对象由于地址不同而被Hibernate视为多条记录。


以上为个人整理,第一次写博客,纰漏在所难免,望海涵。