[
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2668?page=c...
]
Juan Osuna commented on HHH-2668:
---------------------------------
It would appear then that CacheKey is not consistent with the part of the spec you quote
since it does not invoke or depend on equals defined on identifier types.
In the attached test case, I do, in fact, implement equals/hashcode for the identifier
type, ComplexItemPK. However, the problem is still manifested since Hibernate ignores this
implementation and instead invokes equals on the nested components of the identifier type,
but not the identifier type itself.
I also found this in the spec:
"The primary key (or field or property of a composite primary key) should be one of
the following types:
any Java primitive type; any primitive wrapper type; java.lang.String; java.util.Date;
java.sql.Date."
This would seem to indicate that composite id classes may not contain nested complex
entities. If this is the case, then Hibernate should enforce this contract rather than
tolerate it and fail under obscure scenarios. This would prevent naïve, developers from
investing a lot of time going down an incorrect path. By offering the key-many-to-one
mapping under composite id, Hibernate seems to implicitly encourage an approach that
violates the spec.
For those of us who have already gone down this path and are faced with legacy
constraints, it would be nice if Hibernate would do the best it can to act reasonably,
even in the face of these inconsistencies.
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:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira