开发者

Grails Domain Classes in Sets

Is it a bad practice to use domain objects in Sets or as keys in Maps?

In the past I've done things like this a lot

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

However I have noticed that I often encounter problems when findAllByAuthorLike might return a list of Hibernate Proxy objects com.me.Book_$$_javassist_128 but findByTitleLike will return a proper com.me.Book object. This causes duplicates in the set because the real object and the proxy are considered not equal.

I find I need to be extremely careful when using Sets of domain objects like this, and I get the feeling it might be something I should not be doing in the first place.

The alternative is of course to use a set/map of id's, but it makes my code verbose and prone to misunderstanding

Set<Integer> someBooks = [] as Set // a set of id's for books    

@Burt: I thought Grails domain classes already did this, at least so that equals/compare was done on class/id's rather than the object instance. Do you mean a special comparator for hibernate proxies?

return (this.c开发者_开发问答lass == obj.class && this.id == obj.id) || 
       (obj.class  == someHibernateProxy && this.id == obj.id)


It's not bad practice at all, but just like in a non-Grails application you should override equals and hashCode if you'll be putting them in hash-based collections (HashSet, HashMap, etc.) and also implement Comparable (which implies a compareTo method) if you're going to use TreeSet/TreeMap/etc.


Proper implementation of equals() and hashcode() in a Hibernate-backed situation is far from trivial. Java Collections require that hashcodes of objects and behaviour of equals() don't change, but the id of an object can change when it's a newly created object, and other fields can change for a wide variety of reasons. Sometimes you have a good unchangeable business id that you can use, but quite often that's not the case. And obviously default Java behaviour is also not suitable in a Hibernate situation.

The best solution I've seen is described here: http://onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=2

The solution it describes is: initialize the id as soon as the object is created. Don't wait for Hibernate to assign an id. Configure Hibernate to use version to determine whether it's a new object. This way, id in unchangeable, and can be safely used for hashcode() and equals().

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜