Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The JPA hashCode() / equals() dilemma

There have been some discussions here about JPA entities and which hashCode()/equals() implementation should be used for JPA entity classes. Most (if not all) of them depend on Hibernate, but I'd like to discuss them JPA-implementation-neutrally (I am using EclipseLink, by the way).

All possible implementations are having their own advantages and disadvantages regarding:

  • hashCode()/equals() contract conformity (immutability) for List/Set operations
  • Whether identical objects (e.g. from different sessions, dynamic proxies from lazily-loaded data structures) can be detected
  • Whether entities behave correctly in detached (or non-persisted) state

As far I can see, there are three options:

  1. Do not override them; rely on Object.equals() and Object.hashCode()
    • hashCode()/equals() work
    • cannot identify identical objects, problems with dynamic proxies
    • no problems with detached entities
  2. Override them, based on the primary key
    • hashCode()/equals() are broken
    • correct identity (for all managed entities)
    • problems with detached entities
  3. Override them, based on the Business-Id (non-primary key fields; what about foreign keys?)
    • hashCode()/equals() are broken
    • correct identity (for all managed entities)
    • no problems with detached entities

My questions are:

  1. Did I miss an option and/or pro/con point?
  2. What option did you choose and why?



UPDATE 1:

By "hashCode()/equals() are broken", I mean that successive hashCode() invocations may return differing values, which is (when correctly implemented) not broken in the sense of the Object API documentation, but which causes problems when trying to retrieve a changed entity from a Map, Set or other hash-based Collection. Consequently, JPA implementations (at least EclipseLink) will not work correctly in some cases.

UPDATE 2:

Thank you for your answers -- most of them have remarkable quality.
Unfortunately, I am still unsure which approach will be the best for a real-life application, or how to determine the best approach for my application. So, I'll keep the question open and hope for some more discussions and/or opinions.

like image 674
MRalwasser Avatar asked Feb 17 '11 16:02

MRalwasser


People also ask

Do JPA entities need equals and hashCode?

Introduction. As previously explained, using the JPA entity business key for equals and hashCode is always best choice. However, not all entities feature a unique business key, so we need to use another database column that is also unique, as the primary key.

Does JPA use equal?

If you take a look at the JPA specification, you will be surprised to only find 2 explicit and 1 implicit mention of both methods: You need to implement the equals() and hashCode() methods for primary key classes if you map composite primary keys.

In what cases equals () and hashCode () should be overridden when using hibernate?

You only need to override equals() and hashcode() if the entity will be used in a Set (which is very common) AND the entity will be detached from, and subsequently re-attached to, hibernate sessions (which is an uncommon usage of hibernate).

Does hibernate use equals?

In other words, Hibernate uses equals and hashCode for identity, not to see if an object has been modified. It uses attribute by attribute comparisons for that.


2 Answers

Read this very nice article on the subject: Don't Let Hibernate Steal Your Identity.

The conclusion of the article goes like this:

Object identity is deceptively hard to implement correctly when objects are persisted to a database. However, the problems stem entirely from allowing objects to exist without an id before they are saved. We can solve these problems by taking the responsibility of assigning object IDs away from object-relational mapping frameworks such as Hibernate. Instead, object IDs can be assigned as soon as the object is instantiated. This makes object identity simple and error-free, and reduces the amount of code needed in the domain model.

like image 199
Stijn Geukens Avatar answered Sep 20 '22 13:09

Stijn Geukens


I always override equals/hashcode and implement it based on the business id. Seems the most reasonable solution for me. See the following link.

To sum all this stuff up, here is a listing of what will work or won't work with the different ways to handle equals/hashCode: enter image description here

EDIT:

To explain why this works for me:

  1. I don't usually use hashed-based collection (HashMap/HashSet) in my JPA application. If I must, I prefer to create UniqueList solution.
  2. I think changing business id on runtime is not a best practice for any database application. On rare cases where there is no other solution, I'd do special treatment like remove the element and put it back to the hashed-based collection.
  3. For my model, I set the business id on constructor and doesn't provide setters for it. I let JPA implementation to change the field instead of the property.
  4. UUID solution seems to be overkill. Why UUID if you have natural business id? I would after all set the uniqueness of the business id in the database. Why having THREE indexes for each table in the database then?
like image 25
nanda Avatar answered Sep 18 '22 13:09

nanda