persistence@glassfish.java.net

Code review for [Issue 1153] Object can't reference itself using _at_EmbeddedId

From: Wonseok Kim <guruwons_at_gmail.com>
Date: Mon, 16 Oct 2006 15:04:55 +0900

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.
>
>