Grails Domain Classes in Sets

Is it good to use domain objects in Sets or as keys in Maps?

I did a lot of things in the past

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

However, I noticed that problems often occur when findAllByAuthorLike can return a list of Hibernate Proxy objects com.me.Book_$$_javassist_128 , but findByTitleLike will return the correct com.me.Book object. This causes duplicates in the set because the real object and proxies are considered not equal.

I believe that I need to be very careful when using sets of domain objects like this, and I feel that this may be something that I should not do in the first place.

An alternative is, of course, using the / map id set, but that makes my code verbose and misunderstood

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

@Burt: I thought Grails domain classes already did this, at least so that equals / compare runs in class / id and not in the instance of the object. Do you mean a special comparator for sleeping proxies?

 return (this.class == obj.class && this.id == obj.id) || (obj.class == someHibernateProxy && this.id == obj.id) 
+7
source share
2 answers

This is not a bad practice at all, but, as in an application without Grails, you must override equals and hashCode if you will put them in hash collections ( HashSet , HashMap , etc.), and implement Comparable (which implies compareTo method) if you intend to use TreeSet / TreeMap / etc.

+9
source

The correct implementation of equals () and hashcode () in a situation with Hibernate support is far from trivial. Java collections require that the hash codes of the objects and the behavior of equals () do not change, but the identifier of the object may change when it is newly created, and other fields can change for a wide variety of reasons. Sometimes you have a nice immutable business identifier that you can use, but quite often it is not. And, obviously, the default behavior of Java is also not suitable in a sleep mode 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 he describes is: initialize the identifier as soon as the object is created. Do not wait for Hibernate to assign an identifier. Configure Hibernate to use the version to determine if this is a new object. Thus, id is immutable and can be safely used for hashcode () and equals ().

+2
source

All Articles