All 15 equals(), hashCode() & toString() Contracts questions
More in equals(), hashCode() & toString() Contracts
Advertisement
Java › equals(), hashCode() & toString() Contracts

Why is it particularly important for a class's equals() and hashCode() to be based on effectively immutable data specifically when that class is used as a JPA/Hibernate entity, and what special considerations apply to entity identity before it's persisted (has a generated ID)?

Intermediate

Answer

JPA entities present a unique challenge for equals()/hashCode(): using the auto-generated database ID (common and seemingly natural) is problematic because a newly-created, not-yet-persisted entity has a null ID (making two distinct new entities awkwardly 'equal' if ID-based equals() treats null IDs as equal, or requiring special-casing), and worse, an entity's hashCode() based on its ID would change once the ID gets assigned during the persist operation, breaking the immutable-hashCode expectation of hash-based collections if the entity was added to a Set before being saved -- common approaches include using a separate, always-present business/natural key for equality (if one genuinely exists), or accepting the added complexity of correctly handling the transient (unsaved) versus persisted entity states explicitly.
public class Order {
    private Long id;  // null until persisted!
    private String orderNumber;  // business key, present immediately, before persistence

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Order other)) return false;
        return orderNumber != null && orderNumber.equals(other.orderNumber);  // use stable business key, not the DB-generated id
    }
    @Override
    public int hashCode() { return orderNumber != null ? orderNumber.hashCode() : System.identityHashCode(this); }
}
Real-world example A team's JPA entity originally used the auto-generated database ID for equals()/hashCode() and experienced entities silently 'disappearing' from a HashSet after being persisted (since the ID, and therefore the hashCode, changed after being assigned by the database), resolved by switching to a stable business key present even before the entity is saved.

Common follow-ups: What's the recommended approach for entities that genuinely have no natural business key available?;How does this specific JPA entity equality challenge differ from the general mutable-field hashCode risk discussed for regular classes?