该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-11-21
看来你是给我下套了,要用hibernate映射多态会不会有重复ID我倒是不记得了,不是三种策略么?懒得去看,我没法做肯定或者否定。另外多态映射的ID可以手动分配么,手动的时候会不会重复?但这不重要,关键是我的多态树为什么一定树依赖Hibernate来做出这种多态继承映射(有些时候记得我不喜欢他的任何方式),我狠可能是自己每个建个表。我说的isNew并不是特指的那个方法,说的是判断是否为持久过的对象而已。。。
即使不以这个多态都没关系,比如非常多的情况都可能把不同的实体类放到同样的散列表之中,如果不能区别类型就会相互覆盖了。。。 希望不是全在老兄的意料之中,我怕和aggressive的家伙说话,:D |
|
返回顶楼 | |
发表时间:2004-11-21
Readonly 写道 这篇wiki确实很详细,但是它有一个明显的错误:没有列出它用eq/hC with the id property实现的代码,从测试代码上看,在id为null的时候,它的hashCode是相等的,所以才会出现multiple new instances in set不能pass的错误。
而改成用这样的实现策略就能都pass了: ...... readonly,你这isNew实现也倒"新颖",因为除了这个词,我实在想不出更加礼貌的词语了. 何谓丑陋,不至于我用一种id策略,就得写个isNew之类的超类实现就是优雅了吧. 两个实体对象其中一个id为null,就不管三七二十一认为两个不相等. 这是什么逻辑? 或许你可以让存取代码正确,但你这个对象算什么回事? 我们不该为了hibernate而hibernate,更多的,我们是为了让你这个entity处理起来更加象java对象. 而且,isNew的实现,在我看来,是丑陋的.不可靠的. 除了hibernate, 除了hibernate的这种id生成策略,你敢保证你这个对象能够正确存取吗? 多累啊,想来想去,还是要小心点好, 不如一次性的用上你所说的"丑陋"代码,他虽然"丑陋",但他似乎能够应付一切. 哼哼............... |
|
返回顶楼 | |
发表时间:2004-11-22
Readonly 写道 shenli 写道 :P 如果是多态继承树你觉得会不会,有无可能?而且作为统一父类本来就应该考虑各种情况。如果当前不出错,只要没用到,isNew我也可以不要
就知道你要这么说, 偶反问你:多态继承你在Hibernate里面怎么mapping, 会出现id相同的吗? 为什么偶这里有一个isNew()的public方法就是另外的故事了, 和本次讨论无关,偶们在有定论以前先别离题,哼哼 hibernate的id生成策略倒是没有仔细研究,但在其他的一些映射工具多态多表的情况下,id完全可以一样. 仔细想想,hibernate中,即使多态多表的情况下,id生成策略是类层次唯一,那么你的DAO有多大意义? 反正目前只有知道hibernate支持你的想法, 换了别的工具,你的想法就不一样了,还不如直接写到你的代码中,或者不需要DAO,而是直接来个hibernateDAO吧........... dao没有记错的话,主要的功能是为了封装存取代码的. |
|
返回顶楼 | |
发表时间:2004-11-22
完蛋了,这篇有变成大肠贴的趋势了,偶尝试着拉回来吧,先整理一下来龙去脉:
1. weihello说: many-to-many的时候要注意equals()/hashCode()的写法 2. CafeBabe说: 简单地采用ID做为equals()/hashCode()实现策略不就可以解决weihello的问题了么? 3. zingers说: 靠ID比较是不可靠的,如果2个Object的ID为null, 但是实际意义上是相当的, 岂不是错误了? (开始离题了一次) 4. 偶说: 如果2个Object的ID为null, 哪怕它的业务属性的值相当, 偶也认为它们是不等的, 这样做有什么错误么? (注意, 后面的回帖没有人说过偶的这个想法有什么逻辑错误, sigh) 5. weihello回来了: 举出了一篇hibernate网站上的wiki, http://www.hibernate.org/109.html , 来说明基于ID做equals()/hashCode() 是有问题的, 不能满足其中的2个测试. (嗯, weihello是个好同志, 把主题拉回来了) 6. 偶回来了: weihello举了一篇错误的wiki来证明他的观点, 该论据不成立, 用CafeBabe最初给出的代码就能完全通过那4个测试, 偶不怕麻烦地重新贴了一下代码, 建议weihello去测试一下, 而不应该是wiki上说什么就信什么. 7. weihello又回来了: 开始扯一大堆了...... 好了,别扯那么多,偶只想讨论的是: 在Hibernate世界里面, 偶们应该如何采用一个简单,有效的策略来实现equals()/hashCode(), that's all...... 偶的Hibernate in action这本书还没有到, 有看过的人可以说说它里面推荐的做法, kaka. weihello 写道 两个实体对象其中一个id为null,就不管三七二十一认为两个不相等. 这是什么逻辑? 偶也不知道这是一种什么逻辑, 你能举个实际用例的代码来说明它有什么逻辑错误么? |
|
返回顶楼 | |
发表时间:2004-11-22
Readonly 写道 完蛋了,这篇有变成大肠贴的趋势了,偶尝试着拉回来吧,先整理一下来龙去脉:
weihello 写道 两个实体对象其中一个id为null,就不管三七二十一认为两个不相等. 这是什么逻辑? 偶也不知道这是一种什么逻辑, 你能举个实际用例的代码来说明它有什么逻辑错误么? 呵呵,这就是所谓的仰视光芒了。 这篇wiki在我看来是没有你所说错误的,总比实现不伦不类的isNew强吧。 weihello 写道 两个实体对象其中一个id为null,就不管三七二十一认为两个不相等. 这是什么逻辑? 这给代码我想不用贴了吧,难道代码中永远不会出项往map或者set之类中添加两个id为null,实际属性一摸一样的代码? 呵呵,我并非不打算仰视你的光芒。 只是,我想说明的是, 一方面你这种方式会出现以上之类的问题, 一方面你这样写代码不好移植、扩展。 希望你能够理性看待这个问题,你坚持的这种做法是按了葫芦起了瓢,得不偿失。 如果认为我在拉大肠的话,无话可说,只好保持沉默。 |
|
返回顶楼 | |
发表时间:2004-11-22
偶错了, 对于误导观众表示歉意: 按照前面的写法, 那篇文章里面的test, 只能pass前面3个
第4个不能pass: 引用 collections intact after saving: Will the following work or not: HashSet set = new HashSet();; User u = new User();; set.add(u);; session.save(u);; assert(set.contains(u););; 和CafeBabe说的一摸一样, 那么需要反思为什么偶在实际项目中没有遇到过这种用法那? 再想想看有没有其他的方法来通过这个test. |
|
返回顶楼 | |
发表时间:2004-11-22
嗯, 偶又回来了, 想了一个方法来解决原来的问题: 原先基于ID的hashCode()实现, 会在object被持久化以后, ID从null改变到了一个值, 这样hashCode就随之发生了改变, 这个是不符合hashCode语义的地方, 那么偶们来加一个小小的trick, 让它保持不变:
public abstract class Entity implements Serializable { private Long id; private Integer trickHashCode; public boolean isNew(); { return id == 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 (id.equals(((Entity); o);.getId();););; } public int hashCode(); { if (trickHashCode == null); { if (isNew();); trickHashCode = new Integer(super.hashCode(););; else trickHashCode = new Integer(id.hashCode(););; } return trickHashCode.intValue();; } } 这样就pass CafeBabe说的问题(collections intact after saving)了. 以下是unit test pass那个wiki里面说的几种情况: public void test(); { Parent p1 = new Parent();; Child c1 = new Child();; Child c2 = new Child();; p1.addChild(c1);; p1.addChild(c2);; //multiple new instances in set: assertEquals(2, p1.getChildren();.size(););; manager.save(p1);; //equal to same object from another session: Parent p2 = manager.load(p1.getId(););; assertEquals(p1, p2);; //collections intact after saving: Child c3 = new Child();; p2.addChild(c3);; manager.save(p2);; assertEquals(3, p2.getChildren();.size(););; assertTrue(p2.getChildren();.contains(c3););; } 这种简单有效的做法, 目前看起来就只有一个问题了: 这种先save再load一次, 然后和之前的未被持久化的object做mix compare不能通过, Parent p1 = new Parent();; Child c1 = new Child();; p1.addChild(c1);; manager.save(p1);; Parent p2 = manager.load(p1.getId(););; //will fail...... assertTrue(p2.getChildren();.contains(c1););; 实在是想不出来这种比较会出现在什么情境下了...... |
|
返回顶楼 | |
发表时间:2004-11-22
无论优雅于丑陋,符合实际需要就好了。
这个世界上有成千上万的DIRTY CODE在正常而出色的完成自己的使命。 至于这个BasePersistentObject我真不敢想象它在实际应用中的性能,太恐怖了~ |
|
返回顶楼 | |
发表时间:2004-11-22
Readonly,你的后面的这种写法在特定场合不管会不会遇到错误,都已经违反了hashCode基本的原则,当两个对象equals方法成立的时候,必须得到同样的散列码
既然一个对象是可变的,我不知道为什么要保证他的散列码不变呢?这个测试(当然不知道谁规定的)这种可重新读出有意义吗?本来在java环境下就没这个要求,不过是hibernate一种特殊的主键产生策略导致的问题? 刚刚download了hibernate in acdtion的代码,他是在每个entity用业务字段生成的equals和hashCode。原来我也是在一个父类或者mixin中用主键做的,唯一的不同就是用this.getclass == object.getclass取代你的x instance of xxxx |
|
返回顶楼 | |
发表时间:2004-11-23
shenli 写道 Readonly,你的后面的这种写法在特定场合不管会不会遇到错误,都已经违反了hashCode基本的原则,当两个对象equals方法成立的时候,必须得到同样的散列码
嗯, 偶做错了, 这样会导致: 先做了hashCode操作以后一个未被持久化的object, 和再从数据库里面获得的object虽然equals, 但是hashCode不一样了, 违反了最基本的原则, shenli 写道 既然一个对象是可变的,我不知道为什么要保证他的散列码不变呢?这个测试(当然不知道谁规定的)这种可重新读出有意义吗?本来在java环境下就没这个要求,不过是hibernate一种特殊的主键产生策略导致的问题?
嗯, java规范里面好像是没有这样的规定, 但是作为一种特定的可持久化对象 (如hibernate这样对应到数据库里的一个row), 在equals的语义上带来的后果: 2个从不同session里面load的对应到同一row的object, 你认为它们是相等还是不相等呢? shenli 写道 刚刚download了hibernate in acdtion的代码,他是在每个entity用业务字段生成的equals和hashCode。原来我也是在一个父类或者mixin中用主键做的,唯一的不同就是用this.getclass == object.getclass取代你的x instance of xxxx
昨天偶问过看过Hibernate in action的人了, 他说书里面有3种做法: 1. 就是偶说的这种用无意义主键做hashCode/equals 2. 就是weihello用的所有值做hashCode/equals 3. 用一个(或者几个)相对稳定的业务字段做hashCode/equals (比如user, 就用userName). hibernate 推荐的是第3种, 按照这种推荐的做法, 就不会出现以上说的所有问题了, 看起来是最佳的实践了. |
|
返回顶楼 | |