I have JPA entities where some properties are annotated with @Transient
.
Should I use these properties in equals/hashCode/toString
methods?
My first thought is NO but I don't know why.
You only need to override equals() and hashcode() if the entity will be used in a Set (which is very common) AND the entity will be detached from, and subsequently re-attached to, hibernate sessions (which is an uncommon usage of hibernate).
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. It is not required that if two objects are unequal according to the equals(java. lang.
toString() method returns the String representation of an Object. The default implementation of toString() for an object returns the HashCode value of the Object. We'll come to what HashCode is. Overriding the toString() is straightforward and helps us print the content of the Object. @
The case of toString()
is different, you can do whatever you want with toString()
so I will only cover equals()
(and hashCode()
).
First, the rule: if you want to store an object in a List
, Map
or a Set
then it is a requirement that equals
and hashCode
are implemented so they obey the standard contract as specified in the documentation.
Now, how to implement equals()
and hashCode()
? A "natural" idea would be to use the properties mapped as Id
as part of the equals()
:
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if (id==null) return false;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.id.equals( that.getId() );
}
public int hashCode() {
return id==null ? System.identityHashCode(this) : id.hashCode();
}
}
Unfortunately, this solution has a major problem: when using generated identifiers, the values are not assigned until an entity becomes persistent so if a transient entity is added to a Set
before being saved, its hash code will change while it's in the Set
and this breaks the contract of the Set
.
The recommended approach is thus to use the attributes that are part of the business key i.e. a combination of attributes that is unique for each instance with the same database identity. For example, for the User class, this could be the username:
public class User {
...
public boolean equals(Object other) {
if (this==other) return true;
if ( !(other instanceof User) ) return false;
final User that = (User) other;
return this.username.equals( that.getUsername() );
}
public int hashCode() {
return username.hashCode();
}
}
The Hibernate Reference Documentation summarizes this as follow:
"Never use the database identifier to implement equality; use a business key, a combination of unique, usually immutable, attributes. The database identifier will change if a transient object is made persistent. If the transient instance (usually together with detached instances) is held in a
Set
, changing thehashcode
breaks the contract of theSet
. Attributes for business keys don't have to be as stable as database primary keys, you only have to guarantee stability as long as the objects are in the same Set." - 12.1.3. Considering object identity"It is recommended that you implement
equals()
andhashCode()
using Business key equality. Business key equality means that theequals()
method compares only the properties that form the business key. It is a key that would identify our instance in the real world (a natural candidate key)" - 4.3. Implementing equals() and hashCode()
So, back to the initial question:
@Transient
attributes are very likely not part of such a key.List
, Map
, Set
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With