دوشنبه، خرداد ۱۰، ۱۳۸۹

Equality for entities in a Hibernate application - part II


Isn't the new code template in the previous part lovely? ha? In fact it is pretty easy task to do. Check this wonderful post out to do it yourself: Syntax Higlighting

I was reading Effective Java some time ago. It has a section devoted to implementing equals methods and after reading that, I thought it might be better if I revise my equals method. So I changed it to something like this:

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if(!(obj instanceof BaseEntity)) {
return false;
}
BaseEntity o = (BaseEntity) obj;
return getId() == null ?
o.getId() == null : getId().equals(o.getId());
}

The new implementation is more compact and does not need HibernateProxyHelper. But this method does not behave exactly like before. Since it uses instanceof instead of getClass(), if you have a Car and a Dog entity instances, as long as they have equal ids, they are equal! In other words this method may not behave as expected when dealing with objects of a single hierarchy in a Java collection.

instanceOf operator returns false when the object is null so there is no need for null checking.

There is a huge problem in both these implementations. If you are anything like me and use Hibernate's automatic id generation, then the ids are not set until the object is persisted! Our implementation returns true when both ids are null and you should think twice about how equals should be implemented when two entities are not persisted yet. Maybe reference comparison is sufficient but it is largely application specific.

Even I have a bigger question! Is using id a wise comparison method to implement equals method? Even when ids are database auto-generated values? I doubt it. Again consider cat and dog example. I persist each one in a separate table and MySQL uses 1 as start number for each table's id value. So a dog and a cat with ids equal to 1 are equal! The previous version that used getClass() did not have this problem, because it checked the exact uniqueness across tables guarantees uniqueness across classes. (Ids are not equal across whole database, they are just equal in a single table in MySQL)

Perhpas it is better to implement the equals method in children and include some unique business keys if applicable! Like using national id but not all entities have unique business keys. hmm...

How do you implement your equals method on objects in a database application?

۱ نظر:

  1. well I guess as you said it is widely application specific. I believe if you have some business id like SSN it is best to use it in equals method. but when you don't have it subtle problems (like the ones you mentioned) may arise if extra care is not applied. in your case it seems that there is no other choice except sacrificing conciseness (second implementation) for correctness (first one). BTW the part where you mentioned the point about using the getter instead of the property was amazing.

    پاسخحذف