该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-05-27
icewubin 写道 linliangyi2007 写道 兄弟,你说法是对的,但你没有解决我遇到的问题。我的问题是,传入equals方法的对象根本是个所有属性都是null的新对象,跟在equals外取到的对象不是一个东西。
非常郁闷啊,我也不希望是hibernate的bug,要知道,第一,我感情上很喜欢Hibernate;第二,我有大量的应用基于hibernate,它要出问题了,我可郁闷大发了!目前这个症状仅在我程序的一个地方出现;大部分情况是很正常的,but~~~谁知道下一次在哪里啊 兄弟,你啥时候能把我说的测试代码运行一下,我其实不是很关心other.getClass(),我其实更关心this.getClass()。 没问题,不过代码环境在公司,节后一定运行一下!! 再次说明,希望大家把精力集中的问题本身哈。je上发贴就是为了技术而技术,灌水我上天涯,吹牛会去mopper的。 |
|
返回顶楼 | |
发表时间:2009-05-27
CGLIB产生proxy的原理:是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法,在Hibernate中就是调用getter方法时访问数据库进行实际的查询。但是在调用getId时是不会查询数据库的。因为ID已经知道了。
|
|
返回顶楼 | |
发表时间:2009-05-27
linliangyi2007 写道 jianfeng008cn 写道 cglib 替换对象是很正常的,你能不能明确的说明错在哪了?你的替换了对象是什么意思啊?这帖子看得真冒火。
ps:你一个劲强调自己水平好有意义嘛! 我发贴可不强调你说的,这样的说法容易歪楼哈!! 替换了对象的意思就是:cglib给我new了一个所有属性都=null的FlowToken对象,这个对象是FlowToken的一个proxy没错,但什么属性值都取不到,再次强调所有方法返回null。我不知道为啥发生这样的情况。 就当我故意歪楼好了,别搞半天就是楼上说的原因。 |
|
返回顶楼 | |
发表时间:2009-05-27
rrsy23 写道 从这个问题看LZ对java的equlas与hashcode没有理解清楚;
这两个方法是留个自己实现的; 我完全可以写个equals永远返回false,hashcode永远返回一样; 说个实际点的吧; set 放进去的东西能找到吗? package com.bobo; import java.util.HashSet; import java.util.Random; import java.util.Set; public class TestMain { public static void main(String[] args) { new TestMain().test1(); } public void test1() { Set set = new HashSet(); set.add(new A()); // false System.out.println(set.contains(new A())); set.add(new B()); // true System.out.println(set.contains(new B())); set.add(new C()); // true System.out.println(set.contains(new C())); A a = new A(); set.add(a); // true System.out.println(set.contains(a)); B b = new B(); set.add(b); // true System.out.println(set.contains(b)); C c = new C(); set.add(c); // 大部分情况下是false System.out.println(set.contains(c)); } class A { } class B { @Override public int hashCode() { return 1; } @Override public boolean equals(Object obj) { return true; } } class C { @Override public int hashCode() { return new Random().nextInt(); } @Override public boolean equals(Object obj) { return true; } } } 理解哈吧,研究哈吧;其实很多书上也讲了; 关键是你思考没有,研究没有; set.add(new C()); // true System.out.println(set.contains(new C())); 这个的输出结果真是true吗 |
|
返回顶楼 | |
发表时间:2009-05-27
歪个楼,既然楼上的说道hashcode和equals,我来说说我的理解。
如果你使用的容器是HashSet,那么当判断contain的时候,首先容器取的是对象的hashCode方法,而后,在相同hashCode的hash桶中使用equals方法比较。 然而如果你使用的是TreeSet那么比较将使用Comparable接口的comparaTo了,comparaTo返回0就是相等,认为集合内已经存在,及时这时的hashcode和equals方法都不等 |
|
返回顶楼 | |
发表时间:2009-05-28
linliangyi2007 写道 代码如下:
/** * 判断当前流程实例上下文中,是否存在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咯!!! 我重新整理下, 首先这里有2个 token 和 taskInstance.getFlowToken(), 我把参数传入的token 称为 tokenA, 把 taskInstance.getFlowToken() 称为 tokenB. 楼主说 tokenB 是 hibernate 通过lazyload 产生的代理. 而根据 LOC_3 :token == taskInstance.getFlowToken() 打印的 true 的情况来看. 可以知道 tokenA 和 tokenB 都是hibernate代理的对象. 而且是同一个对象. Hibernate lazy load的原理是 用cglib 生成当前 model 的子类. 重写他所有(根据hibernate定义)的 getter 和 setter 方法, 注意是getter, setter的,像equals应该不会被重写, 另外model 的所有final方法不会重写(所以个人很讨厌final, spring 框架里就一堆), 当你调用一个 getter 的时候他才从数据库取出真实的对象返回他的值 因为tokenA和tokenB都是被代理的对象, 所以在equals 直接调用 id 属性的话, 必然返回 null. 也有人说 这个时候hibernate 已经知道对象的id 了, 这个没错. 但知道id 也不一定会把他知道的id 放在id属性中吧. 所以equals内, 必须使用 getId() 来访问id. 楼主一开始没有用getter访问出错了, 后来经过大家指正把代码改成了 13. if (id == null || other.getId() == null) { 14. return false; 15. } else if (!id.equals(other.getId())) 16. return false;可以看到虽然tokenB是通过getter 访问了, 但 tokenA 还是使用id 属性. 这样结果当然还是不对. 所以楼主等节后去公司里试验一下用getter来访问id, 我认为就不会有问题了, 最后我想说的是 这贴我也投了新手. 不是因为问题什么, 而是因为这个标题, 你们竟然会怀疑hibernate有bug. hibernate 这么常用的框架, equals这么常用的功能. 是不会有这么这么大的一个bug 的. 如果真的有的话, 也会立刻被修复. 我像表达的意思是 99.99% 的问题都是我们使用不当造成的, 不应该动不动就质疑框架类库, 这样的风气我认为是不应该被提倡的. |
|
返回顶楼 | |
发表时间:2009-05-28
还行,问题讨论得很清楚,直接obj.id这应该是一种有“隐患”的程序写法,尤其在类增强的情况下,obj.id又属于比较特殊的属性。
被评为新手贴的确有失公允。 |
|
返回顶楼 | |
发表时间:2009-05-28
Sunteya 写道 可以看到虽然tokenB是通过getter 访问了, 但 tokenA 还是使用id 属性. 这样结果当然还是不对.
即使this.id不对,但是楼主先质疑为什么other.getId()为什么是null。(个人觉得other.getId()因该不为空,楼主真的确认了么?) |
|
返回顶楼 | |
发表时间:2009-05-28
被拼新手了啊,可惜了
|
|
返回顶楼 | |
发表时间: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)) |
|
返回顶楼 | |