persistence@glassfish.java.net

RE: Re: Code review for [Issue 1153] Object can't reference itself using @EmbeddedId

From: Gordon Yorke <gordon.yorke_at_oracle.com>
Date: Wed, 18 Oct 2006 14:28:40 -0400

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.