Mappingmedium3-5 years

An entity's `equals`/`hashCode` are generated from its `@GeneratedValue` id. A new `Tag` is added to a `HashSet`, persisted, and then `set.contains(tag)` returns `false` — even though `set.size()` is 1 and iterating returns that exact object. Why, and what should an entity's `equals`/`hashCode` be based on?

Before persist, the id is null, so the hash code is computed from null and the tag is placed in that bucket. persist assigns the id, which changes the hash code, but a HashSet never re-buckets an entry. contains recomputes the hash from the new id, looks in a different bucket, and does not find the object that is still sitting in the old one. Nothing throws, nothing logs. Three implementations work: a real immutable business key (ISBN, email) when one exists; a UUID assigned in the constructor, so the id exists before persist and never changes; or, as a fallback that Hibernate's own documentation suggests, equals on the id with a constant hashCode(), which is always correct but makes lookups in a big hash set linear.

The lesson behind it →
More on Mapping