A HashSet uses hashCode() alongside equals() to store and compare objects. When equality is redefined, both methods must agree: equal instances must return the same hash value. Otherwise, a set may fail to treat equivalent objects as one element. section 8.2.1 section 10.3.2
Listing 8.13 Implementing custom equality with equals() and hashCode()
Path: Ch08/mapping-collections/src/main/java/com/manning/javapersistence /ch08/setofembeddables/Image.java @Embeddable public class Image { // . . . @Override public boolean equals(Object o) { #1 if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Image image = (Image) o; return width == image.width && height == image.height && filename.equals(image.filename) && item.equals(image.item); } @Override public int hashCode() { #2 return Objects.hash(filename, width, height, item); } // . . . } #1 This custom equality check in <kbd>equals()</kbd> compares all values of one <kbd>Image</kbd> to the values of another <kbd>Image</kbd>. If all values are the same, the images must be the same. #2 The <kbd>hashCode()</kbd> method has to fulfill the contract requiring that if two instances are equal, they must have the same hash code.
An @Embeddable Image compares its values in equals() and hashes the same values with Objects.hash(). This gives a set value-based equality rather than the default object-identity comparison. Override both methods for embeddable value types, especially when they are stored in sets or used as map keys. section 8.2.1
Entities need a different decision. Within one persistence context, repeated loads of a database row return the same Java instance. Loads in separate contexts can produce distinct instances representing that row. Custom equals() and hashCode() matter when detached entities must compare equal, particularly in sets or as map keys; merely displaying a detached entity does not require them. section 10.3.1
Choose an entity’s hash input with its lifecycle in mind:
- Avoid a generated database identifier: a transient entity can enter a Set before Hibernate assigns its identifier. Saving it would then change its hash value while it is in the set. section 10.3.2
- Use a business key for custom equality when detached instances must compare equal. Define hashCode() from the same key used by equals(). section 10.3.2
Listing 10.3 Custom implementation of User equality
@Entity
@Table(name = "USERS",
uniqueConstraints =
@UniqueConstraint(columnNames = "USERNAME"))
public class User {
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null) return false;
if (!(other instanceof User)) return false;
User that = (User) other;
return this.getUsername().equals(that.getUsername());
}
@Override
public int hashCode() {
return getUsername().hashCode();
}
// . . .
}
The User implementation compares usernames and returns the username’s hash code; the mapping also declares a database uniqueness constraint for that column. The accompanying detached-instance example shows references loaded in separate persistence contexts occupying one HashSet entry. section 10.3.2

