How should equals and hashcode be implemented when using JPA and Hibernate How should equals and hashcode be implemented when using JPA and Hibernate java java

How should equals and hashcode be implemented when using JPA and Hibernate


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().


I don't think that the accepted answer is accurate.

To answer the original question:

Is the default implementation good enough for most cases?

The answer is yes, in most cases it is.

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).

The accepted answer indicates that the methods need to be overriden if either condition is true.


The best equals and hashCode implementation is when you use a unique business key or natural identifier, like this:

@Entitypublic class Company {    @Id    @GeneratedValue(strategy = GenerationType.AUTO)    private Long id;     @Column(unique = true, updatable = false)    private String name;     @Override    public int hashCode() {        HashCodeBuilder hcb = new HashCodeBuilder();        hcb.append(name);        return hcb.toHashCode();    }     @Override    public boolean equals(Object obj) {        if (this == obj) {            return true;        }        if (!(obj instanceof Company)) {            return false;        }        Company that = (Company) obj;        EqualsBuilder eb = new EqualsBuilder();        eb.append(name, that.name);        return eb.isEquals();    }}

The business key should be consistent across all entity state transitions (transient, attached, detached, removed), that's why you can't rely on id for equality.

Another option is to switch to using UUID identifiers, assigned by the application logic. This way, you can use the UUID for the equals/hashCode because the id is assigned before the entity gets flushed.

You can even use the entity identifier for equals and hashCode, but that requires you to always return the same [hashCode value so that you make sure that the entity hashCode value is consistent across all entity state transitions, like this:

@Entity(name = "Post")@Table(name = "post")public class Post implements Identifiable<Long> {     @Id    @GeneratedValue    private Long id;     private String title;     public Post() {}     @Override    public boolean equals(Object o) {        if (this == o) return true;         if (!(o instanceof Post))            return false;         Post other = (Post) o;         return id != null &&               id.equals(other.getId());    }     @Override    public int hashCode() {        return getClass().hashCode();    }      //Getters and setters omitted for brevity}