JPA, and for that matter Hibernate, depends on identifying entity instances (I’ll refer to them as entities for short) by some key. That key can either be a single property (like a numeric ID column in the corresponding table) or a business key (also referred to as natural key). Business keys are entity properties which not only uniquely identify the entity but also make ‘sense’ in the world outside the database. Examples for business keys are ISBNs, geographic locations or even entire entities (i.e. in an Orwellian company the ID of an employee could be the cubicle entity he’s working in).
Consider a simple entity:
@Entity
class Book{
@Id
Long id;
boolean equals(Object o){
return ((Book)o).id.equaks(id);
}
}
I kept the code simple for clarity (scopes, equals missing instanceof etc). There is a little problem here: unless a Book is persisted first, the id property is null failing equals and hashcode. A Book object essentially is unusable without an id: you can’t add it to a collection and you can’t give it to any 3rd party code without losing your sleep over it.
JPA tells you: unless you really need a business key, don’t implement equals and hashcode which makes absolutely sense: in a session there can be ever only one instance of a particular row in a table. There never can be two book objects with the same ID and the best way to enforce this is to drop the notion of equality on business keys and go with object identity. Thus entities can be added to collections and given to any code without having to distinguish between a persisted or non-persisted entity.
But what if you need to implement a business key? In that case I’ve found it convenient to prepend a ‘reconciliation’ step (e.g. with an aspect) that looks up non-persisted entities in JPA and copies over the new values.