Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I write equals() and hashCode() methods in JPA entities?

I want to check if entity is in a Collection member (@OneToMany or @ManyToMany) of another entity:

if (entity2.getEntities1().contains(entity1)) { } 
like image 989
j2j Avatar asked Dec 08 '10 14:12

j2j


People also ask

Do JPA entities need equals and hashCode?

Introduction. As previously explained, using the JPA entity business key for equals and hashCode is always best choice. However, not all entities feature a unique business key, so we need to use another database column that is also unique, as the primary key.

Do you need to override the equals and hashCode method in your entity classes?

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object. hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

Does JPA use equal?

If you take a look at the JPA specification, you will be surprised to only find 2 explicit and 1 implicit mention of both methods: You need to implement the equals() and hashCode() methods for primary key classes if you map composite primary keys.

What happens if we do not override hashCode () and equals () in HashMap?

If you don't override hashcode() then the default implementation in Object class will be used by collections. This implementation gives different values for different objects, even if they are equal according to the equals() method.


2 Answers

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() and equals() with a business key. That may be a subset of properties that identify the entity. For example, for a User a good business key might be the username or the email. This is considered good practice.

  • override hashCode() and equals() 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.

like image 111
Bozho Avatar answered Sep 22 '22 05:09

Bozho


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 } 
like image 34
Vlad Mihalcea Avatar answered Sep 19 '22 05:09

Vlad Mihalcea