在设置中使用域对象或在地图中使用<?p>是不好的做法?
过去我做过很多这样的事情
Set<Book> someBooks = [] as Set
someBooks.addAll (Book.findAllByAuthorLike('%hofstadter%'))
someBooks.add (Book.findByTitleLike ('%eternal%'))
但是我注意到,当findAllByAuthorLike
可能返回Hibernate代理对象列表com.me.Book_$$_javassist_128
但findByTitleLike
将返回正确的com.me.Book
对象时,我经常会遇到问题。这会导致集合中出现重复,因为真实对象和代理被视为不相等。
我发现在使用像这样的域对象集时我需要非常小心,我觉得它可能是我不应该首先做的事情。
替代方案当然是使用id的set / map,但它会使我的代码变得冗长并容易产生误解
Set<Integer> someBooks = [] as Set // a set of id's for books
@Burt:我认为Grails域类已经这样做了,至少是为了在类/ id而不是对象实例上完成equals / compare。你的意思是hibernate代理的特殊比较器吗?
return (this.class == obj.class && this.id == obj.id) ||
(obj.class == someHibernateProxy && this.id == obj.id)
答案 0 :(得分:9)
这完全不错,但就像在非Grails应用程序中一样,如果要将它们放在基于散列的集合中,则应覆盖equals
和hashCode
{{1}如果您要使用HashSet
/ HashMap
/等,还要实施Comparable
(这意味着compareTo
方法)
答案 1 :(得分:2)
在Hibernate支持的情况下正确实现equals()和hashcode()远非微不足道。 Java Collections要求对象的哈希码和equals()的行为不会改变,但是当它是新创建的对象时,对象的id可能会发生变化,而其他字段可能会因各种原因而发生变化。有时你可以使用一个很好的不可更改的业务ID,但通常情况并非如此。显然,默认的Java行为也不适合Hibernate情况。
我在这里描述了最好的解决方案:http://onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=2
它描述的解决方案是:创建对象后立即初始化id。不要等待Hibernate分配ID。配置Hibernate以使用版本来确定它是否是新对象。这样,id就不可更改了,可以安全地用于hashcode()和equals()。