7

Sets中使用域对象或作为 Maps 中的键是一种不好的做法吗?

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

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<Integer> someBooks = [] as Set // a set of id's for books    

@Burt:我认为Grails 域类已经做到了这一点,至少这样等于/比较是在类/id 而不是对象实例上完成的。你的意思是休眠代理的特殊比较器吗?

return (this.class == obj.class && this.id == obj.id) || 
       (obj.class  == someHibernateProxy && this.id == obj.id)
4

2 回答 2

9

这根本不是一个坏习惯,但就像在非 Grails 应用程序中一样,您应该重写equalshashCode如果您将它们放在基于哈希的集合(HashSetHashMap等)中,并且如果您也实现Comparable(这意味着一种compareTo方法)重新使用TreeSet/ TreeMap/等。

于 2011-06-09T03:09:54.290 回答
2

在 Hibernate 支持的情况下正确实现 equals() 和 hashcode() 绝非易事。Java 集合要求对象的哈希码和 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()。

于 2011-08-24T11:06:33.237 回答