Home » Eclipse Projects » EclipseLink » Order of mapping resolution
Order of mapping resolution [message #380506] |
Tue, 26 August 2008 14:32 |
Tom Denley Messages: 4 Registered: July 2009 |
Junior Member |
|
|
I am observing some interesting side-effects when using back-references
where part of the foreign key is qualified by a grand-parent in the
relationship. This is fairly in-depth, but I will do my best to explain it
succinctly.
I have a three-tier one-to-many hierarchy, described by the following
tables. Each table is keyed on a unique sequence-generated ID, and a
string code which splits the schema vertically.
GRANDPARENT (GRANDPARENT_ID, CODE)
PARENT (PARENT_ID, CODE)
CHIILD (CHILD_ID, CODE)
My object model for this domain gives each object its own identifier, but
stores the code on the grandparent only.
GrandParent
+ int identifier
+ String code
+ Collection<Parent> children
Parent
+ int identifier
+ GrandParent backRef
+ Collection<Child> children
Child
+ int identifier
+ Parent backRef
I map this through EclipseLink (We use the POJO ORM API as we came from
TopLink) using direct-to-field mappings for the identifier fields; batch
read one-to-many mappings for the forward going relationships; and
one-to-one mappings for the back-references.
This appears to work fine, but I noticed that in some scenarios fetches
were retrieving Parent objects whose Child objects were missing.
Debugging through the EclipseLink code, I noticed that for some
one-to-many relationships, EclipseLink is trying to pull child records
(from the pool of previously batch-read child candidates) using a correct
parent sequence number key, but a code of null. Looking further into this,
I discovered that EclipseLink is deriving the code by walking up the
back-reference to the GrandParent, but that this back-reference hadn't
been populated yet and was null. It appears that, for the mapping to work
correctly, the backRef property of each domain object must be the first
one to be populated, before the children property is addressed.
Looking in more detail at the way in which EclipseLink populates its
relational mappings reveals the reason for the erratic behaviour we were
observing. The order in which EclipseLink processes relational mappings is
deemed to be unimportant, and is casually achieved by iterating over the
result of a call to descriptor.getMappings() in the
buildAttributesIntoObject method in
org.eclipse.persistence.internal.descriptors.ObjectBuilder. This simply
returns the key-set of the Map of relationships (keyed by their
descriptors), and hence the order is arbitrarily determined by the hash
values of the descriptors and the hashing function of the JVM. Small
changes to our mappings were causing these hash values to change, and
subsequently the order of relational mapping population to change.
Arguably, this is both a bug in EclipseLink and an oversight in our
domain. Clearly, nobody in EclipseLink has ever considered the order of
population of relational descriptors to be important. There are no efforts
in the code to intelligently calculate the order, and there is no evidence
of any exposure of that order to the user through the EclipseLink IDE. We
should therefore consider that, although entirely achievable, it is
incorrect to build a domain in such a way that the order of relational
mapping evaluation becomes important.
The resolution for us, therefore, has been to re-architect our domain
objects so that the code is propagated throughout, and to adjust the
EclipseLink mappings to make use of these de-normalised codes so as to
prevent the need for back-references to be populated first. However, to me
this represents a pollution of our domain, and for this reason I am
posting here to see if anybody else has experienced similar problems, or
if anyone connected with EclipseLink would like to make a comment.
Regards,
Tom
|
|
| | | | | |
Re: Order of mapping resolution [message #380558 is a reply to message #380542] |
Fri, 29 August 2008 10:40 |
Tom Denley Messages: 4 Registered: July 2009 |
Junior Member |
|
|
I modified my backpointer mappings to use method accessing, and added a
conditional breakpoint to the setter method when it is called with null.
During the database fetch, I am hitting this breakpoint on a Child object
with the following stack trace:
Thread [main] (Suspended (breakpoint at line 100 in Child))
Child.setBackRef(Parent) line: 100
GeneratedMethodAccessor145.invoke(Object, Object[]) line: not available
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 585
PrivilegedAccessController.invokeMethod(Method, Object, Object[]) line:
501
MethodAttributeAccessor.setAttributeValueInObject(Object, Object) line:
126
OneToOneMapping(DatabaseMapping).setAttributeValueInObject(O bject,
Object) line: 1108
OneToOneMapping(ForeignReferenceMapping).buildCloneFromRow(D atabaseRow,
Object, ObjectLevelReadQuery, UnitOfWork, Session) line: 224
ObjectBuilder.buildAttributesIntoWorkingCopyClone(Object,
ObjectLevelReadQuery, DatabaseRow, UnitOfWork, boolean) line: 1107
ObjectBuilder.buildWorkingCopyCloneFromRow(ObjectLevelReadQu ery,
DatabaseRow, UnitOfWork, Vector) line: 1219
ObjectBuilder.buildObjectInUnitOfWork(ObjectLevelReadQuery, DatabaseRow,
UnitOfWork, Vector, Descriptor) line: 410
ObjectBuilder.buildObject(ObjectLevelReadQuery, DatabaseRow) line: 376
ReadAllQuery(ObjectLevelReadQuery).buildObject(DatabaseRow) line: 451
ReadAllQuery(ObjectLevelReadQuery).registerIndividualResult( Object,
UnitOfWork, boolean) line: 1701
ReadAllQuery(ObjectLevelReadQuery).conformIndividualResult(O bject,
UnitOfWork, DatabaseRow, Expression, IdentityHashtable, boolean) line: 615
ReadAllQuery.conformResult(Object, UnitOfWork, DatabaseRow, boolean)
line: 339
ReadAllQuery.registerResultInUnitOfWork(Object, UnitOfWork, DatabaseRow,
boolean) line: 669
ReadAllQuery.executeObjectLevelReadQuery() line: 466
ReadAllQuery(ObjectLevelReadQuery).executeDatabaseQuery() line: 800
ReadAllQuery(DatabaseQuery).execute(Session, DatabaseRow) line: 603
ReadAllQuery(ObjectLevelReadQuery).execute(Session, DatabaseRow) line:
768
ReadAllQuery.execute(Session, DatabaseRow) line: 436
ReadAllQuery(ObjectLevelReadQuery).executeInUnitOfWork(UnitO fWork,
DatabaseRow) line: 825
RepeatableWriteUnitOfWork(UnitOfWork).internalExecuteQuery(D atabaseQuery,
DatabaseRow) line: 2532
RepeatableWriteUnitOfWork(Session).executeQuery(DatabaseQuer y,
DatabaseRow) line: 981
...
NoIndirectionPolicy.valueFromQuery(ReadQuery, DatabaseRow, Session) line:
262
OneToManyMapping(ForeignReferenceMapping).valueFromRow(Datab aseRow,
ObjectLevelReadQuery, Session) line: 1105
OneToManyMapping.valueFromRow(DatabaseRow, ObjectLevelReadQuery, Session)
line: 833
OneToManyMapping(ForeignReferenceMapping).buildCloneFromRow( DatabaseRow,
Object, ObjectLevelReadQuery, UnitOfWork, Session) line: 221
ObjectBuilder.buildAttributesIntoWorkingCopyClone(Object,
ObjectLevelReadQuery, DatabaseRow, UnitOfWork, boolean) line: 1107
ObjectBuilder.buildWorkingCopyCloneFromRow(ObjectLevelReadQu ery,
DatabaseRow, UnitOfWork, Vector) line: 1219
ObjectBuilder.buildObjectInUnitOfWork(ObjectLevelReadQuery, DatabaseRow,
UnitOfWork, Vector, Descriptor) line: 410
ObjectBuilder.buildObject(ObjectLevelReadQuery, DatabaseRow) line: 376
ReadObjectQuery(ObjectLevelReadQuery).buildObject(DatabaseRo w) line: 451
ReadObjectQuery(ObjectLevelReadQuery).registerIndividualResu lt(Object,
UnitOfWork, boolean) line: 1701
ReadObjectQuery(ObjectLevelReadQuery).conformIndividualResul t(Object,
UnitOfWork, DatabaseRow, Expression, IdentityHashtable, boolean) line: 615
ReadObjectQuery.conformResult(Object, UnitOfWork, DatabaseRow, boolean)
line: 321
ReadObjectQuery.registerResultInUnitOfWork(Object, UnitOfWork,
DatabaseRow, boolean) line: 586
ReadObjectQuery.executeObjectLevelReadQuery() line: 403
ReadObjectQuery(ObjectLevelReadQuery).executeDatabaseQuery() line: 800
ReadObjectQuery(DatabaseQuery).execute(Session, DatabaseRow) line: 603
ReadObjectQuery(ObjectLevelReadQuery).execute(Session, DatabaseRow) line:
768
ReadObjectQuery.execute(Session, DatabaseRow) line: 370
ReadObjectQuery(ObjectLevelReadQuery).executeInUnitOfWork(Un itOfWork,
DatabaseRow) line: 825
RepeatableWriteUnitOfWork(UnitOfWork).internalExecuteQuery(D atabaseQuery,
DatabaseRow) line: 2532
RepeatableWriteUnitOfWork(Session).executeQuery(DatabaseQuer y,
DatabaseRow) line: 981
RepeatableWriteUnitOfWork(Session).executeQuery(DatabaseQuer y) line: 938
...
MyRepository.fetch(String, String) line: 220
This suggests that the batch fetch for Child objects is not working
properly. Indeed, looking at the query result, I see that seven Child
instances have been fetched, five of which having their backRef property
correctly populated, and two having it set to null.
|
|
|
Goto Forum:
Current Time: Mon Sep 23 19:24:00 GMT 2024
Powered by FUDForum. Page generated in 0.04557 seconds
|