首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Hibernate Bean的Equals方法重载有关问题

2012-10-28 
Hibernate Bean的Equals方法重载问题代码如下:/** * 判断当前流程实例上下文中,是否存在Blocking(阻挡性)

Hibernate Bean的Equals方法重载问题
代码如下:

/** * 判断当前流程实例上下文中,是否存在Blocking(阻挡性)的任务 * 规则 * 1.任务未结束  * 2.任务是必办理的 * 3.当前任务实例的token和执行上下文的token一致 * @param token * @return */public boolean hasBlockingTaskInstances(FlowToken token) {boolean hasBlockingTasks = false;System.out.println("LOC_1 :"  + token.getId());Set<TaskInstance> taskInstances = flowInstance.getTaskInstances();if (taskInstances!=null) {Iterator<TaskInstance> iter = taskInstances.iterator();while ( (iter.hasNext()) && (!hasBlockingTasks)) {System.out.println("LOC_2 : "  + taskInstance.getFlowToken().getId());System.out.println("LOC_3 : "  + token == taskInstance.getFlowToken());System.out.println("LOC_4 : "  + token.equals(taskInstance.getFlowToken());TaskInstance taskInstance = (TaskInstance) iter.next();if ( (! taskInstance.hasEnded()) //任务未结束 && (taskInstance.getIsBlocking()) //任务是必办理的&& (token!=null) && (token.equals(taskInstance.getFlowToken())) ) {hasBlockingTasks = true;}}}return hasBlockingTasks;}


上述的 LOC_1 处和 LOC_2 处打印的ID完全相等, LOC_3处输出true(说明对象地址相同),BUT~~ LOC_4处输出是false;


给出FlowToken的equals实现
/* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (!(obj instanceof FlowToken))return false;final FlowToken other = (FlowToken) obj;if (id == null || other.id == null) {return false;} else if (!id.equals(other.id))return false;return true;}


跟踪发现,实际进入equals的obj参数根本不是外面传入的那个token对象,中间过程被Hinernate的CGLIBLazyInitializer做了代理处理,发生了底层对象的变换。
开始大家都打死不信有这样的BUG,经过对jvm线程和变量编号的反复跟踪,BUG每次都重现。确认CGLIBLazyInitializer确有问题,但目前在国内互联网上没有找到相同的案例。这里提出,供javaeye上的大牛商讨!

如果我们一群人的诊断是对的,hibernate就隐藏了一个相当恐怖的bug咯!!!
public boolean equels(Object obj){doCglibHandle...super.equels(obj);doCglibHandle...}

而,这里最要注意的是在调试工具里面设置的断点一直都是FlowToken该方法的某一行,而不是FlowToken$Proxy类里面的,(这点你可以在debug时注意看stack,可以看到先被FlowToken$Proxy.equels调了以后才到你的断点),所以,你监控的是父类

再看父类的方法:

public boolean equals(Object obj) {       if (this == obj)           return true;       if (obj == null)           return false;       if (!(obj instanceof FlowToken))           return false;       final FlowToken other = (FlowToken) obj;       if (id == null || other.id == null) {           return false;       } else if (!id.equals(other.id))           return false;       return true;   }  



第一行,为什么不等呢?很简单,这里的this已经是父类的实例,而不是你的token ,所以和传进来的实例自然不等。

那么为什么你看到的传进来的Obj属性全是null,也很简单,因为这个obj也是hibernate的代理出来的一个延时加载类,所有属性都没有的,都是通过get从数据库获得。

不知,这样解释,LZ明白否?

public boolean equels(Object obj){doCglibHandle...super.equels(obj);doCglibHandle...}

而,这里最要注意的是在调试工具里面设置的断点一直都是FlowToken该方法的某一行,而不是FlowToken$Proxy类里面的,(这点你可以在debug时注意看stack,可以看到先被FlowToken$Proxy.equels调了以后才到你的断点),所以,你监控的是父类

再看父类的方法:

public boolean equals(Object obj) {       if (this == obj)           return true;       if (obj == null)           return false;       if (!(obj instanceof FlowToken))           return false;       final FlowToken other = (FlowToken) obj;       if (id == null || other.id == null) {           return false;       } else if (!id.equals(other.id))           return false;       return true;   }  



第一行,为什么不等呢?很简单,这里的this已经是父类的实例,而不是你的token ,所以和传进来的实例自然不等。

那么为什么你看到的传进来的Obj属性全是null,也很简单,因为这个obj也是hibernate的代理出来的一个延时加载类,所有属性都没有的,都是通过get从数据库获得。

不知,这样解释,LZ明白否?




难得有人把事情说清楚了,这样即使被评新手,也认了。

JE上浅薄的人不少啊,还有不少人吧思路放在equals方法的逻辑上,又或者简单的说明proxy中那些谁都明白的概念。
如果问题都那么简单,还用发贴?!
这种问题别不见的人人都碰到并及时发现的。发贴更多是想提醒大家,碰到同样问题的时候,可以很快的醒悟,但很多人在BBS上只是想显摆自己很牛...
动不动就是隐藏和新手,我看je这样走不远(一直希望它能超越CSDN,看来是很困难了)if (!(obj instanceof FlowToken)) return false;

深入研究Java equals方法public boolean equels(Object obj){doCglibHandle...super.equels(obj);doCglibHandle...}

而,这里最要注意的是在调试工具里面设置的断点一直都是FlowToken该方法的某一行,而不是FlowToken$Proxy类里面的,(这点你可以在debug时注意看stack,可以看到先被FlowToken$Proxy.equels调了以后才到你的断点),所以,你监控的是父类

再看父类的方法:

public boolean equals(Object obj) {       if (this == obj)           return true;       if (obj == null)           return false;       if (!(obj instanceof FlowToken))           return false;       final FlowToken other = (FlowToken) obj;       if (id == null || other.id == null) {           return false;       } else if (!id.equals(other.id))           return false;       return true;   }  



第一行,为什么不等呢?很简单,这里的this已经是父类的实例,而不是你的token ,所以和传进来的实例自然不等。

那么为什么你看到的传进来的Obj属性全是null,也很简单,因为这个obj也是hibernate的代理出来的一个延时加载类,所有属性都没有的,都是通过get从数据库获得。

不知,这样解释,LZ明白否?




还有一件事不明白,就是我在跟踪的时候,就像你说的this.id是有值的,但other.getId()依然返回null。
你上面说的,我基本明白了,直接取属性有问题,但通过get方法取id,应该要返回值才是啊?if (!(obj instanceof FlowToken)) return false;

深入研究Java equals方法

感谢提供了这么有用的思路。 确实有这个问题。但就性能而言,尤其是在有了类型约束的Set和Map的容器比较中,我还是偏向于旧的,“带有问题”但高性能的实现。当然,会慎重的考虑到文章中提到的bug(如果出现了逻辑上的不正确,特别会去思考一下,是不是这个问题造成的,呵呵)
if (!(obj instanceof FlowToken)) return false;

深入研究Java equals方法

感谢提供了这么有用的思路。 确实有这个问题。但就性能而言,尤其是在有了类型约束的Set和Map的容器比较中,我还是偏向于旧的,“带有问题”但高性能的实现。当然,会慎重的考虑到文章中提到的bug(如果出现了逻辑上的不正确,特别会去思考一下,是不是这个问题造成的,呵呵)

用getClass()==getClass()做比较就可以常规短路,这是Eclipse代码生成equals()的实现方法。

如果要支持Hibernate代理对象,可以这样:
        if (getClass().getPackage() != other.getClass().getPackage()) {               return false;           } 

至于性能高不高,楼主有空可以自己测一下,我也没细测,但是看过JDK源代码,没什么问题,而且都是引用(内存地址)比较,应该不慢。
76 楼 linliangyi2007 2009-06-02   再说对于帖子标题的看法!

本人一直认为在互联网上的,在BBS上一切都不必太较真。那种拘泥于标题,拘泥于网络形式,而忽视文章内容的做法,不敢苟同。

现在标题改了,算是迎合网络部分较真人士的观念吧!
做人难,现实生活累人,没想到上个bbs现在也很累人!

热点排行