Hello Wonseok,
I reviewed the changes, they look good. I can check it in for you if you would like?
--Gordon
-----Original Message-----
From: Wonseok Kim [mailto:guruwons_at_gmail.com]
Sent: Wednesday, October 18, 2006 4:50 AM
To: persistence_at_glassfish.dev.java.net
Subject: Re: Code review for [Issue 1153] Object can't reference itself using @EmbeddedId
Gordon, could you review this?
Regards
- Wonseok
On 10/16/06, Wonseok Kim <guruwons_at_gmail.com> wrote:
Hi team,
I believe you had a good weekend.
I'm trying to fix the below issue 1153. The situation and cause of this issue are explained in the below message, please see it.
To fix this, I added a map member to ObjectBuilder, which holds the secondary key field(primary key field of the child table) and primary key field(of the root table) mappings.
// Secondary key field to Primary key field map (joined inheritance or secondary table)
protected Map primaryKeyFieldsBySecondaryField;
This map is used in ObjectBuilder.extractValueFromObjectForField() to get the corresponding primary key field when the child entity's primary key field was given.
/**
* Extract the value of the primary key attribute from the specified object.
*/
public Object extractValueFromObjectForField(Object domainObject, DatabaseField field, AbstractSession session) throws DescriptorException {
// Allow for inheritance, the concrete descriptor must always be used.
ClassDescriptor descriptor = null;//this variable will be assigned in the final
if (getDescriptor().hasInheritance() && (domainObject.getClass() != getDescriptor().getJavaClass()) && ((descriptor = session.getDescriptor(domainObject)).getJavaClass() != getDescriptor().getJavaClass())) {
return descriptor.getObjectBuilder().extractValueFromObjectForField(domainObject, field, session);
} else {
DatabaseMapping mapping = getMappingForField(field);
if (mapping == null) {
throw DescriptorException.missingMappingForField(field, getDescriptor());
}
// GF#1153
// If this entity is a child entity with JOINED inheritance strategy and Embedded Id,
// getting value with the primary key field of child table won't succeed
// for AggregateObjectMapping which only knows fields of root entity's table.
// Therefore we use the primary key field of the root entity.
if(getDescriptor().isChildDescriptor() && mapping.isAggregateObjectMapping()){
DatabaseField primaryKeyField = (DatabaseField) getPrimaryKeyFieldsBySecondaryField().get(field);
if(primaryKeyField != null) {
field = primaryKeyField;
}
}
return mapping.valueFromObject(domainObject, field, session);
}
}
I also added a new test for this - it required new models in 'inheritance' package because there is no proper entity for this issue.
All entity-persistence tests including the new test passed with this fix.
There are diffs, all modified and new files in the attached file.
Please review!
Thanks
- Wonseok Kim
On 13 Oct 2006 11:54:28 -0000, guruwons_at_dev.java.net < guruwons_at_dev.java.net> wrote:
https://glassfish.dev.java.net/issues/show_bug.cgi?id=1153
------- Additional comments from guruwons_at_dev.java.net Fri Oct 13 11:54:28 +0000 2006 -------
This exception occurs for all types of relationships whose target is a child
entity which has JOINED inheritance strategy and Embedded Id.
Following are an exception example.
----------
Caused by: Exception [TOPLINK-45] (Oracle TopLink Essentials - 9.1 (Build )):
oracle.toplink.essentials.exceptions.DescriptorException
Exception Description: Missing mapping for field [ NATURALPERSON.ID].
Descriptor: RelationalDescriptor(jpatests.model.Guid --> [DatabaseTable(PERSON)])
at
oracle.toplink.essentials.exceptions.DescriptorException.missingMappingForField(DescriptorException.java :885)
at
oracle.toplink.essentials.internal.descriptors.ObjectBuilder.extractValueFromObjectForField(ObjectBuilder.java:1584)
at
oracle.toplink.essentials.mappings.AggregateObjectMapping.valueFromObject (AggregateObjectMapping.java:912)
at
oracle.toplink.essentials.internal.descriptors.ObjectBuilder.extractValueFromObjectForField(ObjectBuilder.java:1587)
at
oracle.toplink.essentials.mappings.OneToOneMapping.writeFromObjectIntoRow (OneToOneMapping.java:1058)
at
oracle.toplink.essentials.internal.descriptors.ObjectBuilder.buildRow(ObjectBuilder.java:732)
at
oracle.toplink.essentials.internal.descriptors.ObjectBuilder.buildRow (ObjectBuilder.java:720)
at
oracle.toplink.essentials.internal.queryframework.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:425)
at
oracle.toplink.essentials.queryframework.InsertObjectQuery.executeCommit (InsertObjectQuery.java:74)
at
oracle.toplink.essentials.internal.queryframework.DatabaseQueryMechanism.performUserDefinedWrite(DatabaseQueryMechanism.java:635)
at
oracle.toplink.essentials.internal.queryframework.DatabaseQueryMechanism.performUserDefinedInsert (DatabaseQueryMechanism.java:599)
at
oracle.toplink.essentials.internal.queryframework.DatabaseQueryMechanism.insertObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:495)
at
oracle.toplink.essentials.queryframework.WriteObjectQuery.executeCommitWithChangeSet (WriteObjectQuery.java:130)
at
oracle.toplink.essentials.internal.queryframework.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:283)
at
oracle.toplink.essentials.queryframework.WriteObjectQuery.executeDatabaseQuery (WriteObjectQuery.java:67)
at
oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:609)
at
oracle.toplink.essentials.queryframework.DatabaseQuery.executeInUnitOfWork (DatabaseQuery.java:536)
at
oracle.toplink.essentials.queryframework.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:123)
at
oracle.toplink.essentials.queryframework.ObjectLevelModifyQuery.executeInUnitOfWork (ObjectLevelModifyQuery.java:95)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2218)
at
oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery (AbstractSession.java:937)
at
oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:894)
at
oracle.toplink.essentials.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet (CommitManager.java:254)
at
oracle.toplink.essentials.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:175)
at
oracle.toplink.essentials.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet (AbstractSession.java:2638)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1030)
at
oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork.commitToDatabase (RepeatableWriteUnitOfWork.java:357)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1112)
at
oracle.toplink.essentials.internal.ejb.cmp3.base.RepeatableWriteUnitOfWork.commitRootUnitOfWork (RepeatableWriteUnitOfWork.java:82)
at
oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:842)
at
oracle.toplink.essentials.internal.ejb.cmp3.transaction.base.EntityTransactionImpl.commit (EntityTransactionImpl.java:90)
... 26 more
----------
CAUSE
If entity B is a child entity of entity A which has composite primary key by
@EmbeddedId, the internal class descriptor of B inherit mappings from the
descriptor of A. Therefore B has same AggregateObjectMapping instance of entity
A, and the AggregateObjectMapping instance has only fields of entity A. For
example, if the primary key fields of A are A.ID1 and A.ID2 (table 'A'), and the
primary key fields of B are B.ID1 and B.ID2, the AggregateObjectMapping has only
A.ID1 and A.ID2.
If ObjectBuilder.extractValueFromObjectForField() is called to get primary key
value of child entity B with B.ID1, the AggregateObjectMapping doesn't find
corresponding mapping with B.ID1 because it only knows A.ID1 and A.ID2. So an
Exception like belwo is thrown.
[TOPLINK-45] oracle.toplink.essentials.exceptions.DescriptorException
Exception Description: Missing mapping for field [B.ID1].
So we need to call ObjectBuilder.extractValueFromObjectForField() of
AggregateObjectMapping with the field A.ID1 instead of given B.ID1.
I'm working on the fix.