If you don’t like the way we need to implement equals()
and hashCode()
for JPA entities, then there is a different route you can take.
When you generate the primary key before you create the object, there are 2 advantages:
-
The id
can be made required in the constructor so you can’t create "invalid" objects.
-
The equals() and hashCode() methods can be simplified to just take the id
into account.
In code, we can imagine this entity:
import org.springframework.util.Assert;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Book {
@Id
private Long id;
private String name;
protected Book() {
}
public Book(Long id,
String name) {
Assert.notNull(id, "id should not be null");
Assert.notNull(name, "name should ot be null");
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The Book
entity does not have the @GeneratedValue
annotation, so we will need to pass in a value at construction time.
Now that we know the id
field is never null
, we can use this implementation:
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Book book = (Book) o;
return id.equals(book.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
We just use id
for equals()
, and we can relay on id
as well for hashCode()
A test on equals could look like this:
@Test
void testEquals() {
Book book1 = new Book(1L, "Taming Thymeleaf");
Book book2 = new Book(1L, "Taming Thymeleaf");
assertThat(book1).isEqualTo(book2);
}
Since we only test the id, this test will also succeed:
@Test
void testEquals() {
Book book1 = new Book(1L, "Taming Thymeleaf");
Book book2 = new Book(1L, "Totally different title");
assertThat(book1).isEqualTo(book2);
}
This might be counter-intuative at first, but this is really what you want.
Entities are defined by their id, when the id is the same, we are talking about the same thing.