0% found this document useful (0 votes)
64 views

Ultimate Guide To Implementing Equals and Hashcode With Hibernate

The document discusses different approaches for implementing the equals() and hashCode() methods for entities in Java and Hibernate based on the type of identifier used. If a natural or business key is present, those values can be used in the implementation. If the primary key is generated, a fixed hash code should be returned instead. When the business key includes a parent reference, both values must be considered in equals() and hashCode().

Uploaded by

Adolf
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
64 views

Ultimate Guide To Implementing Equals and Hashcode With Hibernate

The document discusses different approaches for implementing the equals() and hashCode() methods for entities in Java and Hibernate based on the type of identifier used. If a natural or business key is present, those values can be used in the implementation. If the primary key is generated, a fixed hash code should be returned instead. When the business key includes a parent reference, both values must be considered in equals() and hashCode().

Uploaded by

Adolf
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Implementing equals() and hashCode()

Java’s default implementation of the equals() and hashCode() methods


are based on the object’s identity. So, no two objects are equal and all
of them have a different hash code value.
If you consider that Hibernate makes sure to return the same object,
if you read the same entity twice within a Session, these equals() and
hashCode() implementations are OK as long as an entity stays in the
context of one Session.
But that changes as soon as you work with multiple Sessions or
detach and merge an entity, e.g., by sending it to or retrieving it from
a remote client.

How to implement equals() and hashCode()


An entity object represents a record in a database table. Each of
these records is identified by a unique primary key value and some of
them also have a unique business key. So, it shouldn’t be a surprise if
I tell you that you can use these values in your equals() and
hashCode() implementation. The complexity of this implementation
depends on the kind of key that’s available for your entity.

Using a Business Key or Natural Key


The implementation of your equals() and hashCode() methods is
pretty easy if your entity has a mandatory business or natural key. As
long as this key gets always set during the creation of the entity
object and is immutable, you can base your implementation on this
key. And because the key identifies the object, you don’t need to
include any other entity attributes in your equals() or hashCode()
method.

www.thoughts-on-java.org
Implementing equals() and hashCode()
@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@NaturalId
private String businessKey;

public MyEntity(String businessKey) {


this.businessKey = businessKey;
}

private MyEntity() {}

@Override
public int hashCode() {
return Objects.hashCode(businessKey);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEntity other = (MyEntity) obj;
return Objects.equals(businessKey, other.getBusinessKey());
}
}

www.thoughts-on-java.org
Implementing equals() and hashCode()
Please note that the only public constructor of the MyEntity class
requires a value for the businessKey attribute. The no-args
constructor is private. This is a Hibernate-specific implementation
that’s not supported by the JPA specification. It ensures that the
businessKey attribute is always set and that the hash code of the
object will not change.
If you want to implement this in a JPA-compliant way, you need to
provide a public, no-args constructor. You then need to accept that
the hash code of the object changes when you set the businessKey
attribute or you need to use a fixed hash code as I do for generated
primary key values. In general, a changed hash code doesn’t create
any problems as long as you set the value of the businessKey
attribute before you add the entity object to any Set.

Using a Business Key with a Parent Reference


If you use a business key, it happens quite often that it’s only unique
if you combine it with a reference to a parent entity. You then need
to fetch the parent association eager, include the hash code of the
parent entity in your hash code calculation and also check the
equality of the referenced parent objects in your equals() method.
@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@NaturalId
private String businessKey;

@ManyToOne
private MyParent parent;

public MyEntity(String businessKey) {


this.businessKey = businessKey;
}
www.thoughts-on-java.org
Implementing equals() and hashCode()
public MyEntity(String businessKey) {
this.businessKey = businessKey;
}

private MyEntity() {}

@Override
public int hashCode() {
return Objects.hash(parent, businessKey);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEntity other = (MyEntity) obj;
return Objects.equals(parent, other.getParent())
&& Objects.equals(businessKey, other.getBusinessKey());
}
}

Using a Programmatically Managed Primary Key


If you manage your primary key values programmatically, you can
implement your equals() and hashCode() methods in almost the same
way as I showed you in the previous example. The only requirement

www.thoughts-on-java.org
Implementing equals() and hashCode()
here is that you set the primary key value in the constructor or
immediately after you instantiated a new entity object.

@Entity
public class MyEntity {

@Id
private Long id;

public MyEntity(Long id) {


this.id = id;
}

private MyEntity() {}

@Override
public int hashCode() {
return Objects.hashCode(id);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEntity other = (MyEntity) obj;
return Objects.equals(id, other.getId());
}
}

www.thoughts-on-java.org
Implementing equals() and hashCode()
Using a Generated Primary Key
As I teased earlier, generated primary keys create a problem for the
implementation of your equals() and hashCode() methods. That’s
because the primary key value gets set when the entity gets
persisted. So, your entity object can exist with and without a primary
key value.
The challenge here is that the hash code of your entity isn’t allowed
to change after you added the object to a Set. So, you can’t use the
primary key to calculate the hash code. You need to return a fixed
value that’s the same for all objects of the entity class. That, of
course, negatively affects the performance of very huge Sets and
Maps because they put all objects into the same hash bucket. But
Hibernate can’t efficiently manage huge associations anyways and
you should avoid them in general.

@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Override
public int hashCode() {
return 13;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEntity other = (MyEntity) obj;
return id != null && id.equals(other.getId());
www.thoughts-on-java.org
Implementing equals() and hashCode()

if (getClass() != obj.getClass())
return false;
MyEntity other = (MyEntity) obj;
return id != null && id.equals(other.getId());
}
}

www.thoughts-on-java.org

You might also like