集合中的Grails域类

时间:2011-06-09 02:41:52

标签: grails groovy domain-object

设置中使用域对象或在地图中使用<?p>是不好的做法?

过去我做过很多这样的事情

Set<Book> someBooks = [] as Set
someBooks.addAll (Book.findAllByAuthorLike('%hofstadter%'))
someBooks.add (Book.findByTitleLike ('%eternal%'))

但是我注意到,当findAllByAuthorLike可能返回Hibernate代理对象列表com.me.Book_$$_javassist_128findByTitleLike将返回正确的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)

2 个答案:

答案 0 :(得分:9)

这完全不错,但就像在非Grails应用程序中一样,如果要将它们放在基于散列的集合中,则应覆盖equalshashCode {{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()。