论坛首页 Java企业应用论坛

实现实体的hashCode,equals时候请注意

浏览 46701 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-11-20  
嗯,看来误区很多,偶来详细解释一下:
1. 为什么偶们需要equals()和hashCode()
首先偶们知道,大多数的java object都提供了内建的equals()和hashCode()方法,用于对象识别,如果是基于内存的java object,那么每次偶们new()一个object的时候,它们都是不同的,这2个内建的方法工作得很好,偶们是不用去关心它的。

那么为什么在Hibernate的世界里面,这2个家伙会变得特别呢?

偶们知道Hibernate使用session来管理唯一性,当你使用new()一个对象以后,调用session.save()来把它持久化,那么在session open的周期里,hibernate有能力管理这个对象:当你查询,更新,删除的时候,hibernate都可以返回对应的这个实例,equals()和hashCode()也能够运行得和普通的Object一样。

但是如果session close后,那么就是另外一个世界了:hibernate已经无法知道有这样一个detached object了,比如你现在open了另外一个session,load了同样一个object,这个时候内建的equals()和hashCode()已经无法适用这种情况了,所以偶们才需要考虑override这2个家伙了。

2. 如何简单地处理掉这2个讨厌的家伙?
(要出门了,偶简单说一下, 回来再详细解释):
A. 采用统一的非业务主键,依据主键值是否相同为唯一标准
B. UnSaved Value 的设置
C. one-to-many and many-to-many的设置
0 请登录后投票
   发表时间:2004-11-20  
weihello 写道
CafeBabe 写道
weihello 写道
比较一下ID就行了吗?

   呵呵,我是不会这样写的。 至于原因,你都想当然了,我也不说了。

嘿嘿,你也仔细想想吧
Readonly这回跟我比较一致呀



   谁和你一致都无关,即使是上帝

  如果上帝是错的,我也会和他理论.

   即使上帝是对的,但未能说服我,那么我还会继续理论.

   搞技术总不能人云亦云, 是这样就是这样,不是这样就是这样.

   这儿不是官场.........

晕,偶只是调侃一下,你。。。

比较id我只发现有一个缺陷,就是用HashSet的时候
P p1 = new P();;

C c1 = new C();;
C c2 = new C();;

p1.getCs();.add(c1);;
p1.getCs();.add(c2);;

dao.save(p1);;

System.out.println(p1.getCs();.contains(c1););;
System.out.println(p1.getCs();.contains(c2););;

最后十有89打出两个false,但这种情况会出现吗?
只有当你用swing做client,用rmi把p1序列化到client端,
后来client又把这个p1传回给你。。。
还好,偶们用hessian传输。
0 请登录后投票
   发表时间:2004-11-20  
Readonly 写道

A. 采用统一的非业务主键,依据主键值是否相同为唯一标准
B. UnSaved Value 的设置
C. one-to-many and many-to-many的设置


A中是否就是 CafeBabe 所说呢?

还是等你回来之后详解吧,呵呵
0 请登录后投票
   发表时间:2004-11-21  
Readonly 写道
嗯,看来误区很多,偶来详细解释一下:
1. 为什么偶们需要equals()和hashCode()
首先偶们知道,大多数的java object都提供了内建的equals()和hashCode()方法,用于对象识别,如果是基于内存的java object,那么每次偶们new()一个object的时候,它们都是不同的,这2个内建的方法工作得很好,偶们是不用去关心它的。


   关于hibernate的这部分equals,hashCode实现问题,建议你去看看
http://www.hibernate.org/109.html 
   这里面非常详尽,我强制我的团队必须写equals和hashCode。只是我有基类提供继承。除非没有办法才自己写。可以这么说, 如果只是写写ID的比较,很多java风格的代码都无法写了。 

  这里原话我引述:
引用
The most natural idea that comes to mind is implementing equals() and hashCode() by comparing the property you mapped as a database identifier (ie. the primary key attribute). This will cause problems, however, for newly created objects, because Hibernate sets the identifier value for you after storing new objects. Each new instance therefore has the same identifier, null (or <literal>0</literal>). For example, if you add some new objects to a Set........




   还有,组建类必须写好equals和hashCode,否则会出问题,这在前面有个帖子我遇上过,Shine发现了症结所在。

  脱离hibernate而言, 如果我们不在实体和组件写equals和hashCode写逐个属性比较, 我们还剩下多少地方写这些东东? 没有比这更加迫切需要了......
0 请登录后投票
   发表时间:2004-11-21  
CafeBabe 写道


晕,偶只是调侃一下,你。。。

比较id我只发现有一个缺陷,就是用HashSet的时候


       呵呵,当时只是气你一句话就教训了别人, 不要放在心上。是我不该太认真了。不过我建议你表达的方式也变化一下。

引用
用得着这么复杂吗?比较一下id不就得了。



加一句:  既然用了rmi,建议你们还是用dto吧。

     仅仅web应用或许可以不需要dto,如果是真正分布式的对象,直接传输实体的开销是巨大的。 DTO开销比起实体来说,小多了。

     我的Ivroy框架也是有hessian这一块的,不过,对客户端我们完全隔离实体。
0 请登录后投票
   发表时间:2004-11-21  
weihello 写道

   关于hibernate的这部分equals,hashCode实现问题,建议你去看看
http://www.hibernate.org/109.html 

这篇wiki确实很详细,但是它有一个明显的错误:没有列出它用eq/hC with the id property实现的代码,从测试代码上看,在id为null的时候,它的hashCode是相等的,所以才会出现multiple new instances in set不能pass的错误。

而改成用这样的实现策略就能都pass了:
public abstract class Entity implements Serializable {
    private Long id;

    public boolean isNew(); {
        return getId(); == null;
    }

    public boolean equals(Object o); {
        if (this == o);
            return true;

        if (o == null);
            return false;

        if (!(o instanceof Entity););
            return false;

        if (this.isNew(); || ((Entity); o);.isNew(););
            return false;

        return (getId();.equals(((Entity); o);.getId();););;
    }
    
    public int hashCode(); {
        if(id != null); return id.hashCode();;
        return super.hashCode();;
    }
}


所以结论就是:你前面说的这些应用都不需要那样丑陋的EqualsBuilder,反射之类的实现,用id的equals()/hashCode()是简洁,有效的方法。

如果大家觉得有什么潜在的问题,那么象那个wiki里面,试试看写个测试用例来extends偶上面那个Entity跑跑看就明白了,all pass......
0 请登录后投票
   发表时间:2004-11-21  
如果两个不同的实体类都继承Entity,当他们有相同的ID是用此方法出错了。。。

放在父类要直接比较class
0 请登录后投票
   发表时间:2004-11-21  
shenli 写道
如果两个不同的实体类都继承Entity,当他们有相同的ID是用此方法出错了。。。

放在父类要直接比较class


这种代码会出现在一个正常应用里面??
Cat cat = dao.load(1);;
Girl girl = dao.load(1);;
// 什么样的用户需求会逼得你竟然需要做这样傻瓜式的比较??
if(cat.equals(girl););{
  System.out.println("Catgril is coming......");;
}


所以偶的BaseEntity才不做这样多余的判断呢,
0 请登录后投票
   发表时间:2004-11-21  
:P 如果是多态继承树你觉得会不会,有无可能?而且作为统一父类本来就应该考虑各种情况。如果当前不出错,只要没用到,isNew我也可以不要
0 请登录后投票
   发表时间:2004-11-21  
shenli 写道
:P 如果是多态继承树你觉得会不会,有无可能?而且作为统一父类本来就应该考虑各种情况。如果当前不出错,只要没用到,isNew我也可以不要

就知道你要这么说, 偶反问你:多态继承你在Hibernate里面怎么mapping, 会出现id相同的吗?

为什么偶这里有一个isNew()的public方法就是另外的故事了, 和本次讨论无关,偶们在有定论以前先别离题,哼哼
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics