集合中的Grails域类



集合中使用域对象还是在映射中使用键是一种糟糕的做法?

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

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

@Burt:我认为Grails域类已经做到了这一点,至少equals/compare是在class/id而不是对象实例上完成的。你是说hibernate代理的特殊比较器吗?

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

这不是一个糟糕的做法,但就像在非Grails应用程序中一样,如果要将equalshashCode放在基于哈希的集合(HashSetHashMap等)中,则应该重写它们;如果要使用TreeSet/TreeMap/等,则还应该实现Comparable(这意味着compareTo方法)。

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

相关内容

  • 没有找到相关文章

最新更新