tags:

views:

1091

answers:

2

How should model class's equals and hashcode be implemented in Hibernate? What are the common pitfalls? Is the default implementation good enough for most cases? Is there any sense to use business keys?

It seems to me that it's pretty hard to get it right to work in every situation, when lazy fetching, id generation, proxy, etc are taken into account.

A: 

Yeah, it's hard. In my project equals and hashCode both rely on the id of the object. The problem of this solution is that neither of them works if the object has not been persisted yet, as the id is generated by database. In my case that's tolerable since in almost all cases objects are persisted right away. Other than that, it works great and is easy to implement.

Carlos
What I think we did is to use object identity in the case where the id has not been generated
Kathy Van Stone
the problem here is that if you persist the object, your hashcode changes. That can have big detrimental results if the object is already part of a hash based data structure. So, if you do wind up using object identity, you'd better continue using obj id until the object is completely freed (or remove the object from any hash based structures, persist, then add it back in). Personally, I think it would be best to not use id, and base the hash on immutable properties of the object.
Kevin Day
+8  A: 

Hibernate has a nice and long description of when / how to override equals() / hashCode() in documentation

The gist of it is you only need to worry about it if your entity will be part of a Set or if you're going to be detaching / attaching its instances. The latter is not that common. The former is usually best handled via:

  1. Basing equals() / hashCode() on a business key - e.g. a unique combination of attributes that is not going to change during object (or, at least, session) lifetime.
  2. If the above is impossible, base equals() / hashCode() on primary key IF it's set and object identity / System.identityHashCode() otherwise. The important part here is that you need to reload your Set after new entity has been added to it and persisted; otherwise you may end up with strange behavior (ultimately resulting in errors and / or data corruption) because your entity may be allocated to a bucket not matching its current hashCode().
ChssPly76
When you say "reload" @ChssPly76 you mean doing a `refresh()`? How does your entity, which obeys the `Set` contract end up in the wrong bucket (assuming you have a good enough hashcode implementation).
non sequitor
Refresh the collection or reload the entire (owner) entity, yes. As far as wrong bucket goes: a) you add new entity to set, its id is not set yet so you're using identityHashCode which places your entity in bucket #1. b) your entity (within set) is persisted, it now does have an id and thus you're using hashCode() based on that id. It's different from above and **would have** placed your entity in the bucket #2. Now, assuming you hold a reference to this entity elsewhere, try calling `Set.contains(entity)` and you'll get back `false`. Same goes for get() / put() / etc...
ChssPly76
Makes sense but never used identityHashCode myself though I see it used in the Hibernate source like in their ResultTransformers
non sequitor