Should I write equals() and hashCode() methods in JPA entities?
Yes, you should!
If you don't override the default Java.lang.Object
equals
and hashCode
implementation:
@Entity(name = "Book")
public class Book implements Identifiable<Long> {
@Id
@GeneratedValue
private Long id;
private String title;
//Getters and setters omitted for brevity
}
the merge
operation will return a different object instance and the equality contract is going to be broken.
The best way is to use a business key, like this:
@Entity
public class Book implements Identifiable<Long> {
@Id
@GeneratedValue
private Long id;
private String title;
@NaturalId
private String isbn;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return Objects.equals(getIsbn(), book.getIsbn());
}
@Override
public int hashCode() {
return Objects.hash(getIsbn());
}
//Getters and setters omitted for brevity
}
You can also use the identifier for equality, but mind that the hashCode
implementation should always return the same value, which for entities is not really a problem since you don't fetch many entities per DB transaction, as otherwise, the cost of fetching data is orders of magnitude larger than the single-bucket HashMap
penalty imposed by using a fixed hashCode
:
@Entity
public class Book implements Identifiable<Long> {
@Id
@GeneratedValue
private Long id;
private String title;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return Objects.equals(getId(), book.getId());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
//Getters and setters omitted for brevity
}
Yes, you should define corresponding equals()
and hashcode()
methods, but you should NEVER let the id be part of either. (See this recent answer of mine in a similar question)
Not necessarily. There are three options:
don't override - thus you will be working with instances. This is fine in cases when you are working with the collections with only entities that are attached to the session (and hence guaranteed to be the same instance). This is (for me) the preferred way in many cases, because it requires less code and less consideration when overriding
override
hashCode()
andequals()
with a business key. That may be a subset of properties that identify the entity. For example, for aUser
a good business key might be theusername
or theemail
. This is considered good practice.override
hashCode()
andequals()
using the ID field only. This is fine in some cases, especially if you have a manually-assigned identifier (like an UUID). It is also fine if your entity will never go into a collection. But for transient entities (with no identifier) that go into collections, it causes problems, so careful with this option. As seanizer noted - you should avoid it. Generally, always, unless you are really aware of what you are doing (and perhaps documenting it)
See this article for more details. Also note that equals()
and hashCode()
are tied and should be implemented both with exactly the same fields.
There is information in the Hibernate documentation on this topic.