`
linliangyi2007
  • 浏览: 1012698 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

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)
	 */
	@Override
	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;
	}


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

如果我们一群人的诊断是对的,hibernate就隐藏了一个相当恐怖的bug咯!!!
分享到:
评论
76 楼 linliangyi2007 2009-06-02  
再说对于帖子标题的看法!

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

现在标题改了,算是迎合网络部分较真人士的观念吧!
做人难,现实生活累人,没想到上个bbs现在也很累人!
75 楼 linliangyi2007 2009-06-02  
leadyu 写道
linliangyi2007 写道

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



通过你现在给的一些条件,很难判断为什么other.getId()依然返回null。毕竟token的所有getter,setter,都已经被hibernate代理了,cglib实现了suround级的方法代理,hibernate可以去调super的对应方法,也可以不调,取决于他的实现,现在需要确定几个事情:


1)token和你跟踪到的equels方法里的other是否同一个对象?如果是,没道理other.getId()为null呀,你在外面已经print一次token.getId()了。

2)other.getId()是否调了super.getId();这个通过你在getId设置断点就可以知道,你所设的断点,我前面也说了都是针对super类的。




一切水落石出了。
在equals方法中,this对象的类是FlowToken,而other对象的类是proxy。this.id是有值的,other.id为null,但使用other.getId()是会取到lazy的id值的(之前估计这里测试的时候有问题,居然没取到值。)
一切如leadyu所预测的那样。
74 楼 icewubin 2009-05-31  
jianfeng008cn 写道
到底原因是什么呢,既然搞清楚了,为何不写写清楚?

楼主只是说思路清楚了,估计还没开始调试呢。
73 楼 jianfeng008cn 2009-05-31  
到底原因是什么呢,既然搞清楚了,为何不写写清楚?
72 楼 linliangyi2007 2009-05-31  
leadyu 写道
linliangyi2007 写道

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



通过你现在给的一些条件,很难判断为什么other.getId()依然返回null。毕竟token的所有getter,setter,都已经被hibernate代理了,cglib实现了suround级的方法代理,hibernate可以去调super的对应方法,也可以不调,取决于他的实现,现在需要确定几个事情:


1)token和你跟踪到的equels方法里的other是否同一个对象?如果是,没道理other.getId()为null呀,你在外面已经print一次token.getId()了。

2)other.getId()是否调了super.getId();这个通过你在getId设置断点就可以知道,你所设的断点,我前面也说了都是针对super类的。




十分感谢,思路已经清楚了。后面就是沿着思路把问题摸清楚了
71 楼 leadyu 2009-05-31  
linliangyi2007 写道

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



通过你现在给的一些条件,很难判断为什么other.getId()依然返回null。毕竟token的所有getter,setter,都已经被hibernate代理了,cglib实现了suround级的方法代理,hibernate可以去调super的对应方法,也可以不调,取决于他的实现,现在需要确定几个事情:


1)token和你跟踪到的equels方法里的other是否同一个对象?如果是,没道理other.getId()为null呀,你在外面已经print一次token.getId()了。

2)other.getId()是否调了super.getId();这个通过你在getId设置断点就可以知道,你所设的断点,我前面也说了都是针对super类的。


70 楼 linliangyi2007 2009-05-31  
非常赞同使用getClass()==getClass()做比较,看了你推荐的帖子后,正有此意!!
简单且相对了instanceof更合理,但又不用反射每个属性那么耗性能
69 楼 icewubin 2009-05-31  
linliangyi2007 写道
icewubin 写道
要说equals方法逻辑问题,不知道楼主说的哪个部分。

下面这个短路运算就是有问题的,虽然和这贴讨论的东西没有任何关系。
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源代码,没什么问题,而且都是引用(内存地址)比较,应该不慢。
68 楼 linliangyi2007 2009-05-31  
icewubin 写道
要说equals方法逻辑问题,不知道楼主说的哪个部分。

下面这个短路运算就是有问题的,虽然和这贴讨论的东西没有任何关系。
if (!(obj instanceof FlowToken))      
        return false;


深入研究Java equals方法


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

System.out.println("LOC_3 : "  + token == taskInstance.getFlowToken());  

这说明token 和taskInstance.getFlowToken()  得到的是同一个对象,都是Cglib的代理类。

那么在这过程中有几个类呢?

第一有FlowToken  和 FlowToken$Proxy类(Cglib创建的),而token是FlowToken$Proxy的实例。

那么Cglib怎么实现代理的呢?它并不会修改原类的任何部分,只会新创建一个类,复写所有父类的public方法

那么在FlowToken$Proxy里面equels就变成:

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,应该要返回值才是啊?
66 楼 icewubin 2009-05-31  
要说equals方法逻辑问题,不知道楼主说的哪个部分。

下面这个短路运算就是有问题的,虽然和这贴讨论的东西没有任何关系。
if (!(obj instanceof FlowToken))      
        return false;


深入研究Java equals方法
65 楼 linliangyi2007 2009-05-31  
melode11 写道
你自己的equals逻辑错了。只要你对象的id == null,你就不管两个实际上相不相等,直接return false。
  if (id == null || other.id == null)

应该改成

  if ((id == null || other.id == null) &&!(id ==null&&other.id==null))


你这种逻辑根本不是我要的,对我而言要的业务逻辑是就当id为空时,对象相等比较是无意义的,应设置为不相等。
而且你的写法根本解决不了我提的问题。

不要动不动就说人家的逻辑错了,你根本不知道我要的equals逻辑是啥,又凭什么说我错呢!!
64 楼 linliangyi2007 2009-05-31  
leadyu 写道
首先,我们看看这句:

System.out.println("LOC_3 : "  + token == taskInstance.getFlowToken());  

这说明token 和taskInstance.getFlowToken()  得到的是同一个对象,都是Cglib的代理类。

那么在这过程中有几个类呢?

第一有FlowToken  和 FlowToken$Proxy类(Cglib创建的),而token是FlowToken$Proxy的实例。

那么Cglib怎么实现代理的呢?它并不会修改原类的任何部分,只会新创建一个类,复写所有父类的public方法

那么在FlowToken$Proxy里面equels就变成:

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,看来是很困难了)
63 楼 icewubin 2009-05-30  
icewubin 写道
给所有人建议,hibernate中取ID有更好的通用方法,明天我来贴代码,可以放在BaseModel中的。

http://www.iteye.com/topic/397854
62 楼 leadyu 2009-05-30  
首先,我们看看这句:

System.out.println("LOC_3 : "  + token == taskInstance.getFlowToken());  

这说明token 和taskInstance.getFlowToken()  得到的是同一个对象,都是Cglib的代理类。

那么在这过程中有几个类呢?

第一有FlowToken  和 FlowToken$Proxy类(Cglib创建的),而token是FlowToken$Proxy的实例。

那么Cglib怎么实现代理的呢?它并不会修改原类的任何部分,只会新创建一个类,复写所有父类的public方法

那么在FlowToken$Proxy里面equels就变成:

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明白否?

61 楼 leadyu 2009-05-30  
这个问题有意思,不应该被投新手帖的呀,这个问题并不是大家所想的那样,还要从Cglib的内部机制和java调试JDA两方面造成的。我来说说真正的原因吧。
60 楼 icewubin 2009-05-29  
啊,楼主至少还要再次修改两个id的用法,id都换成getId()。
59 楼 melode11 2009-05-29  
你自己的equals逻辑错了。只要你对象的id == null,你就不管两个实际上相不相等,直接return false。
  if (id == null || other.id == null)

应该改成

  if ((id == null || other.id == null) &&!(id ==null&&other.id==null))
58 楼 KimShen 2009-05-28  
被拼新手了啊,可惜了
57 楼 icewubin 2009-05-28  
Sunteya 写道
可以看到虽然tokenB是通过getter 访问了, 但 tokenA 还是使用id 属性. 这样结果当然还是不对.

即使this.id不对,但是楼主先质疑为什么other.getId()为什么是null。(个人觉得other.getId()因该不为空,楼主真的确认了么?)

相关推荐

    重载equals方法示例

    重载equals方法示例重载equals方法示例重载equals方法示例重载equals方法示例重载equals方法示例

    重写equals方法

    例如,public boolean equals(Object o) 是一个正确的重写方法,而 public boolean equals(String o) 是一个重载方法,而不是重写方法。 equals 方法的实现 ------------------------- equals 方法的实现需要遵循...

    equals方法的重写.docx

    ### equals方法重写知识点解析 #### 一、equals方法简介 `equals`方法是Java语言中Object类的一个重要成员方法,其默认实现是比较两个对象的内存地址是否相同(即是否为同一个对象)。为了使对象之间能够基于内容...

    Java中equals方法隐藏的陷阱

    ### Java中equals方法隐藏的陷阱 在Java编程中,正确实现`equals`方法至关重要,它不仅影响对象的比较逻辑,还直接关系到集合类(如`HashSet`、`HashMap`等)的行为。本文将深入探讨Java中`equals`方法的一些常见...

    set接口经常用的hashCode和equals方法详解

    ### set接口中hashCode和equals方法详解 #### 一、引言 在Java编程语言中,`Set`接口作为集合框架的重要组成部分,在实现无重复元素的数据结构方面扮演着关键角色。为了确保元素的唯一性,`Set`接口依赖于对象的`...

    hashcode和equals方法

    equals()和hashcode()这两个方法都是从object类中继承过来的。当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法.

    重写toString和equals方法

    Java 对象的toString和equals方法重写 在 Java 中,每个对象都继承自 Object 类,而 Object 类中定义了两个重要的方法:toString() 和 equals()。这两个方法都是非常重要的,它们分别用于对象的字符串表示和对象...

    js equals方法

    在JavaScript中,`equals`方法并不像在其他某些编程语言中那样是内置的,例如Java。但是,由于JavaScript的灵活性,我们可以自定义一个`equals`函数来实现对象间的深度比较。这个方法通常用于比较两个对象的属性和值...

    tableTobean.rar_CZAF_K7W_TabletoBean_hibernate反向生成bean

    5. 设定生成Bean的模板,可以选择是否生成equals()、hashCode()、toString()等方法,以及字段的访问级别等。 6. 完成设置后,点击“Finish”按钮,开发工具将自动生成对应的Java Bean类和Hibernate映射文件(.hbm....

    Java编程中避免equals方法的隐藏陷阱介绍

    equals方法的四个常见陷阱分别是:定义了错误的equals方法签名,重载了equals的但没有同时重载hashCode的方法,建立在会变化字域上的equals定义,不满足等价关系的equals错误定义。下面将对每个陷阱进行详细的解释...

    java中的==和equals()方法1

    在Java编程语言中,了解如何正确使用`==`和`equals()`方法是非常关键的,因为它们在比较对象和基本类型时有不同的行为。下面将详细解释这两个方法的工作原理、使用场景以及一些常见误区。 首先,`==`运算符主要用于...

    知识点 比较运算符==和equals方法的比较

    在Java编程语言中,比较运算符`==`和`equals()`方法是用来检查两个对象是否相等的,但它们之间存在显著的区别。理解这些差异对于编写正确的代码至关重要。 首先,我们来看`==`运算符。它主要用于基本数据类型的比较...

    java中hashcode()和equals()方法详解

    - 为了避免哈希冲突带来的性能问题,在覆盖`equals()`方法的同时,通常也需要覆盖`hashCode()`方法。 #### 实现示例 以下是一个简单的类`Person`,展示了如何正确覆盖`equals()`和`hashCode()`方法: ```java ...

    2.javaequals()方法.zip

    2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2.javaequals()方法.zip2....

    关于Object中equals方法和hashCode方法判断的分析

    "关于Object中equals方法和hashCode方法判断的分析" 在 Java 中,Object 类提供了两个重要的方法:equals 方法和 hashCode 方法。这两个方法都是用于比较两个对象是否相等的,但它们的实现机理和作用域却有所不同。...

    ==运算符和Equals()方法区别

    字符串是一个特殊的引用类型,在C#语言中,重载了字符串对象的很多方法,包括`Equals()`方法,使字符串对象用起来就像是值类型一样。这也是为什么在上面的例子中,`a`和`b`的比较结果都是`true`的原因。 需要注意的...

    ==和equals方法究竟有什么区别

    在Java编程语言中,`==`和`equals()`方法是用来比较对象之间关系的两种常见方式,但它们在使用上有着显著的区别。 首先,`==`运算符主要用于比较基本类型(如int、char、byte等)的值是否相等,或者比较引用类型...

    C# Equals 和 GetHashCode 方法重写

    ### C# Equals 和 GetHashCode 方法重写 在C#编程中,`Equals` 和 `GetHashCode` 方法是非常重要的成员方法,它们对于确保对象的正确比较以及高效地存储和检索对象至关重要。这两个方法通常需要在自定义类中进行...

    重写equals和hashcode方法_equals_重写equals和hashcode方法_

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是Object类中的两个核心方法,所有类都默认继承自Object类。这两个方法在处理对象比较和集合操作时起着至关重要的作用。当我们创建自定义类并需要对对象进行精确...

    equals问题经典

    这里我们将深入探讨`equals()`方法的用法,以及它与`==`的区别,同时解决题目中提出的问题。 **问题一:** 在Java中,`String`类的实例有两种创建方式。一种是直接通过字面量,如`String s1 = "abc";`,这时字符串...

Global site tag (gtag.js) - Google Analytics