that uses ==
outside of a persistence
context might see unexpected
results. This might occur even in some unexpected
places, for example, if you put two detached instances into the same Set. Both might have the same database identity ( they represent the same row), but JVM
identity is by definition not guaranteed for instances in detached state.
The developer has to override the equals() and hashCode() methods in persistent classes and implement his own notion of object equality.
There is one caveat: 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 entity is made persistent (see the contract of the persist() operation). If the transient instance (usually together with detached instances) is held in a Set, changing the hashcode breaks the contract of the Set.
Attributes for good 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. See the Hibernate website for a more thorough discussion of this issue. Also note that this is not a Hibernate issue, but simply how Java object identity and equality has to be implemented.
Then for objects attached to a particular persistence context (i.e. in the scope of an EntityManager) the two notions are equivalent, and JVM identity for database identity is guaranteed by the Hibernate Entity Manager. However, while the application might concurrently access the "same" (persistent identity) business object in two different persistence contexts, the two instances will actually be "different" (JVM identity). Conflicts are resolved using (automatic versioning) at flush/commit time, using an optimistic approach.
This approach leaves Hibernate and the database to worry about concurrency; it also provides the best scalability, since guaranteeing identity in single-threaded units of work only doesn't need expensive locking or other means of synchronization. The application never needs to synchronize on any business object, as long as it sticks to a single thread per EntityManager. Within a persistence context, the application may safely use == to compare entities.
An application may concurrently access the same persistent state in two different persistence contexts. However, an instance of a managed class is never shared between two persistence contexts. Hence there are two different notions of identity:
foo.getId().equals( bar.getId() )