The best equals
and hashCode
implementation is when you use a unique business key or natural identifier, like this:
@Entity
public 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
}