]
Christian Bauer commented on HHH-2668:
--------------------------------------
"While it is good practice to implement equals/hashcode, Hibernate does not generally
force users to do this. "
This is not correct, Hibernate requires that identifier types implement equality
correctly. So does the EJB 3.0 specification (JPA), see section 2.1.4.
Caching Fails with Composite-Ids Containing Nested, Complex Entities
--------------------------------------------------------------------
Key: HHH-2668
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2668
Project: Hibernate3
Issue Type: Bug
Components: caching (L2)
Affects Versions: 3.2.4.sp1
Environment: Problem is independent of environment or platform and most likely
exists in prior versions.
Reporter: Juan Osuna
Attachments: hibernate-caching-fix.zip
Original Estimate: 0 minutes
Remaining Estimate: 0 minutes
Description of Failing Test Case Scenario
Preconditions: An entity class is mapped that uses a composite-id that contains a nested
entity class. Only the composite-id class implements equals/hashcode, not the nested
entity class.
Steps to Reproduce:
1. open session and fetch object using composite-id
2. open new session and fetch same object again using different instance of composite-id
but with same identity
Invalid Postconditions: On second retrieve, Hibernate fails to get the object from the
cache and unnecessarily reloads the object. CachKeys containing different instances of the
composite-id always fail to be equal even though they have the same persistent identity.
Attachment Contents
Code fix is attached as well as a Junit test case that reproduces the problem and
validates the fix. The full Hibernate suite was also executed with no impact.
Attachment contains:
New Test Method:
org.hibernate.test.cache.BaseCacheProviderTestCase.testQueryCacheComplexItem
New Test Entity Items:
org\hibernate\test\cache\ComplexItem.hbm.xml
org.hibernate.test.cache.ComplexItem
org.hibernate.test.cache.ComplexItemPK
Code Fix:
org.hibernate.cache.CacheKey (see FIX comments)
Problem and Fix Details
Hibernate generally strives to use persistent identifiers for managing object identity
rather than the equals/hashcode methods implemented by entity classes. While it is good
practice to implement equals/hashcode, Hibernate does not generally force users to do
this.
When wrapping a composite-id object, the current implementation of CacheKey fails to
recurse through nested complex entities to query for equality based on persistent
identity. Instead, when the recursion algorithm hits a complex entity, it invokes equals
directly on that entity rather than further recursing through the identifier object.
Notably, the recursion logic for equals is not symmetrical with the recursion logic for
hashcode, which does recurse through identifier objects. So, while CacheKey never invokes
hashcode on nested complex entities, it does invoke equals on these entities.
A simple fix to this inconsistency is to store the factory parameter passed to CacheKey
and later pass that parameter to the overloaded method:
Type.isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor
factory).
This fix restores symmetry to equals and hashcode behavior. By calling this overloaded
method, the thread of execution will enter EntityType. isEqual(Object x, Object y,
EntityMode entityMode, SessionFactoryImplementor factory), which correctly recurses
through complex identifiers.
Design Principles
Hibernate should strive to behave predictably even in scenarios where users do not follow
best practices.
Hibernate should strive to be as forgiving as possible as long there is no negative
consequence caused by such forgiveness.
Hibernate should behave as consistently as possible. If Hibernate does not generally rely
user-implemented equals/hashcode, it is best to avoid exceptions to this rule wherever
possible.
Possible Future Enhancement
Mapping composite-ids that contain complex entities can cause deep object graphs to be
cached as part of CacheKey. This is unsettling because of it's potential to consume
memory unnecessarily and unpredictably.
Currently, CacheKey caches the hashcode by recursing through a complex graph of
identifier objects. Perhaps, it would also be possible for CacheKey to cache an object
graph of identifier objects whose leaves hold primitive values. This would further add
symmetry between hashcode and equals and lighten the load for caching composite-ids that
hold entity classes.
Robustly supporting composite-ids that hold complex identifiers seems like a worthwhile
design goal.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: