<< Back to previous view

[GLASSFISH-1153] Object can't reference itself using @EmbeddedId Created: 18/Sep/06  Updated: 30/Nov/10

Status: Reopened
Project: glassfish
Component/s: entity-persistence
Affects Version/s: 9.0pe
Fix Version/s: 9.1pe_dev

Type: Bug Priority: Minor
Reporter: keinhaar Assignee: guruwons
Resolution: Unresolved Votes: 2
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Operating System: Windows XP
Platform: Other


File Attachments: Microsoft Word issue1153_fix.diff    
Issuezilla Id: 1,153
Tags:
Participants: guruwons, keinhaar, Peter Salomonsen, sanandal, Sanjeeb Sahoo, sherryshen and tware

 Description   

I have a class hierarchy which uses an embeddedId. NaturalPerson is derived from
Person. NaturalPerson has a List of children where each child is again a
NaturalPerson.
Inheritance is declared as JOINED.

This works fine if Guid is replaced by an String or integer id, but does not
work if the Persons id is an EmbeddedId like Guid. (see error message at the
bottom of this post)

see the forum for a short discussion with more information:
http://forums.java.net/jive/thread.jspa?messageID=154177&#154177

this is a bug, because the id should be found even if it has been declared on
the superclass.

This are the relevant parts of the classes:
@Embeddable class Guid
{
@Id public String getId(){...}
...
}

@Entity class Person
{
@EmbeddedId public Guid getId(){...}
}

@Entity class NaturalPerson extends Person
{
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable( joinColumns=@JoinColumn(name="parent_id",referencedColumnName="id"),
inverseJoinColumns=@JoinColumn(name="child_id",referencedColumnName="id")
)
public List<NaturalPerson> getChildren(){...}
}

javax.persistence.RollbackException: Exception [TOPLINK-45] (Oracle TopLink
Essentials - 2006.8 (Build 060829)):
oracle.toplink.essentials.exceptions.DescriptorException
Exception Description: Missing mapping for field [NATURALPERSON.id].
Descriptor: RelationalDescriptor(me.utils.Guid --> [DatabaseTable(PERSON)])



 Comments   
Comment by keinhaar [ 28/Sep/06 01:40 AM ]
      • Issue 1153 has been confirmed by votes. ***
Comment by guruwons [ 13/Oct/06 04:21 AM ]

I'm looking into this.

  • Wonseok Kim
Comment by guruwons [ 13/Oct/06 04:22 AM ]

Started to work on this.

Comment by guruwons [ 13/Oct/06 04:54 AM ]

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.

Comment by tware [ 19/Oct/06 01:04 PM ]

Created an attachment (id=530)
diff file for fix

Comment by tware [ 19/Oct/06 01:05 PM ]

Fix by Wonsoek Kim checked in.

Comment by sherryshen [ 07/Nov/06 11:14 PM ]

Verified in 9.1pe_b24.

Comment by Sanjeeb Sahoo [ 14/Nov/06 09:06 PM ]

Wonseok,

You mentioned the following in your comments where you explained the CAUSE of
the bug:
"If entity B is a child entity 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."

I don't understand how B can have separate primary key columns than A when B
extends A. Even when JOINED inheritance strategy is used, won't B have the same
PK columns as A?

– Sahoo

Comment by guruwons [ 15/Nov/06 04:38 AM ]

I meant primary keys in table perspective. B has B.ID1 and B.ID2 columns which
correspond to primary keys columns of A - A.ID1 and A.ID2. They are actually
used as foreign keys to table A for joining. B.ID1 and B.ID2 actually don't need
be primary keys in table B, but they are generated as primary keys by DDL
generation. Also they have a generated foreign key constraint (B.ID1, B.ID2) ->
(A.ID1, A.ID2).
Their mappings are maintained in ClassDescriptor.additionalTablePrimaryKeyFields
member as same as secondary table cases.

Comment by Sanjeeb Sahoo [ 15/Nov/06 07:01 PM ]

Hi Wonseok,

Although Table B has ID1 and ID2 as primary key columns and those two columns
are also foreign keys to A.ID1 and A.ID2, but the IDs for the derived entity B
should still be mapped to A.ID1 and A.ID2. That's my understanding. In the
example below(thanks to Sherry Shen who brought this use case to my attention)
where the columns in derived classes have been renamed to PK1 and PK2
respectively, should the referencedColumnNames in @JoinTable be using PK1 and
PK2 or ID1 and ID2? I think, they should be using ID1 and ID2 as shown below.

@Embeddable class Person {
String id1;
String id2;
}

@Entity
@Inheritance(strategy=JOINED)
class Person {
@EmbeddedId PersonPK personPK;
...
}

@Entity
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name="PK1", referencedColumnName="ID1"),
@PrimaryKeyJoinColumn(name="PK2", referencedColumnName="ID2")
})
public class NaturalPerson {
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name="PARENT_CHILDREN",
joinColumns={ @JoinColumn(name="P_ID1", referencedColumnName="ID1"), @JoinColumn(name="P_ID2", referencedColumnName="ID2") },
inverseJoinColumns={ @JoinColumn(name="C_ID1", referencedColumnName="ID1"), @JoinColumn(name="C_ID2", referencedColumnName="ID2") }
)
List<NaturalPerson> children;
}

With the current fix in place, this does not work. It works when we use PK1 and
PK2 as the referenced column names in @JoinTable.

Thanks,
Sahoo

Comment by guruwons [ 16/Nov/06 03:46 AM ]

Interesting,
the issue is not confined to EmbeddedId but applicable to all relationships with
subclasses which has joined inheritance strategy. So I think this is another
issue and need to be discussed in mailing list (probably with spec leads?).

Thanks for bringing this up, Sahoo. I will send another email to mailing list.

Comment by Peter Salomonsen [ 30/Sep/08 06:08 AM ]

This problem still exist in glassfish-v2ur2. We are trying to use JOINED
inheritance strategy, and experience that the SQL generated from TOPLINK creates
two joins to the inherited table, but only references one of the PK columns.

"..... LEFT OUTER JOIN PolygonArea t1 ON (t1.NAME = t0.NAME) LEFT OUTER JOIN
PolygonArea t1 ON (t1.NAME = t0.NAME) ....."

While it rather should have t1.NAME = t0.NAME AND t1.CUSID = t0.CUSID, and not
two equal repeated joins...

Comment by sanandal [ 11/Jan/09 07:01 AM ]

"Reclassifying as P4 because this issue is not deemed "must fix" for this v2.1
release whose primary release driver is SailFin.
This issue will be scrubbed after this release and will be given the right
priority for the next release."





Generated at Fri Apr 25 07:20:03 UTC 2014 using JIRA 4.0.2#472.