persistence@glassfish.java.net

Re: "Invalid composite primary key specification"

From: Marina Vatkina <Marina.Vatkina_at_Sun.COM>
Date: Tue, 07 Aug 2007 18:05:14 -0700

Hi Jeffrey,

What is the Id of the Entity?

thanks,
-marina

Jeffrey Blattman wrote:
> hi marina, thanks for your response.
>
> Marina Vatkina wrote:
>
>> Hi Jeffrey,
>>
>> Jeffrey Blattman wrote:
>>
>>> hi marina,
>>>
>>> as i mentioned below, toplink complains if i include only the single
>>> @JoinColumn for ENTITY_ID and ENTITY_NAME.
>>>
>>> /Exception Description: The @JoinColumns on the annotated element
>>> [private com.sun.portal.pom.Entity
>>> com.sun.portal.pom.Preference.entity] from the entity class [class
>>> com.sun.portal.pom.Preference] is incomplete. W*hen the source entity
>>> class uses a composite primary key, a @JoinColumn must be specified
>>> for each join column using the @JoinColumns.* Both the name and the
>>> referenceColumnName elements must be specified in each such @JoinColumn.
>>> /
>>> as the message says and i indicated below i needed to add a
>>> @JoinColumn for each field of the composite ID.
>>
>>
>> Yes, if Entity has a composite ID. But then it seems that Entity's ID
>> column names do not match the ones that you reference.
>
> Page contains 0 or more Entity contains 0 or more Preference
>
> Entity and Page have composite IDs to enforce the containment model. the
> error above and the @JoinColumn/s code below is from Preference.
> Preference has @Id's name (PREFERENCE_NAME) and entityId (ENTITY_ID).
>
>>
>>>
>>> if i leave off the @JoinColumn/s all together it does work, but i
>>> want to specify the insertable and updatable = false setting and i
>>> assume those are not the defaults for a @ManyToOne mapped field.
>>
>>
>> Then just specify them without specifying the column names.
>>
> notice that the error message says:
>
> /Exception Description: The @JoinColumns on the annotated element
> [private com.sun.portal.pom.Entity com.sun.portal.pom.Preference.entity]
> from the entity class [class com.sun.portal.pom.Preference] is
> incomplete. W*hen the source entity class uses a composite primary key,
> a @JoinColumn must be specified for each join column using the
> @JoinColumns.* *Both the name and the referenceColumnName elements must
> be specified in each such @JoinColumn.*/
>
> ? it's telling me i have to include the column name. i can zip the
> project up so you can see all of it if that helps.
>
>> thanks,
>> -marina
>>
>>>
>>> ?
>>>
>>> Marina Vatkina wrote:
>>>
>>>> Jeffrey,
>>>>
>>>> You @JoinColumn should reference the PK on the ENTITY table, which
>>>> is most probably just ENTITY_NAME.
>>>>
>>>> Now, if you use TopLink to generate the tables, you do not need the
>>>> mapping annotation at all - TopLink will do the right thing for you.
>>>>
>>>> Regards,
>>>> -marina
>>>>
>>>> Jeffrey Blattman wrote:
>>>>
>>>>> after playing with this some, i found the following works ... p.s.,
>>>>> note that i have an entity class w/ the name "Entity" ... so don't
>>>>> let that confuse you :)
>>>>>
>>>>> /_at_IdClass(com.sun.portal.pom.PreferenceId.class)
>>>>> @javax.persistence.Entity
>>>>> public class Preference implements Serializable {
>>>>> @Id
>>>>> @Column(name="PREFERENCE_NAME", nullable=false)
>>>>> private String name;
>>>>> @Id
>>>>> @Column(nullable = false, name="ENTITY_ID")
>>>>> private String entityId = null;
>>>>> @ManyToOne(optional = false, cascade = CascadeType.ALL)
>>>>> * @JoinColumns({
>>>>> @JoinColumn(insertable=false, updatable=false,
>>>>> nullable=false, name="ENTITY_ID", referencedColumnName="ENTITY_NAME"),
>>>>> @JoinColumn(insertable=false, updatable=false,
>>>>> nullable=false, name="PREFERENCE_NAME",
>>>>> referencedColumnName="PREFERENCE_NAME")
>>>>> })
>>>>> * private Entity entity;/
>>>>>
>>>>> i am however seeing this at runtime ...
>>>>>
>>>>> /[TopLink Warning]: 2007.08.06
>>>>> 03:37:48.226--ServerSession(4926903)--Exception [TOPLINK-4002]
>>>>> (Oracle TopLink Essentials - 2.0 (Build 56 (07/18/2007))):
>>>>> oracle.toplink.essentials.exceptions.DatabaseException
>>>>> Internal Exception: java.sql.SQLException: Constraint
>>>>> 'PREFERENCEENTITYID' is invalid: there is no unique or primary key
>>>>> constraint on table 'APP.ENTITY' that matches the number and types
>>>>> of the columns in the foreign key.
>>>>> Error Code: -1
>>>>> Call: ALTER TABLE PREFERENCE ADD CONSTRAINT PREFERENCEENTITYID
>>>>> FOREIGN KEY (ENTITY_ID, PREFERENCE_NAME) REFERENCES ENTITY
>>>>> (ENTITY_NAME, PREFERENCE_NAME)
>>>>> /
>>>>> not sure if it is related?
>>>>>
>>>>> Jeffrey Blattman wrote:
>>>>>
>>>>>> hi again markus,
>>>>>>
>>>>>> Markus Fuchs wrote:
>>>>>>
>>>>>>> Hi Jeffrey,
>>>>>>>
>>>>>>> Jeffrey Blattman wrote:
>>>>>>>
>>>>>>>> thanks markus,
>>>>>>>>
>>>>>>>> this gets me further. the complexity is this, i essentially have
>>>>>>>> three entities
>>>>>>>>
>>>>>>>> 1. Page
>>>>>>>> 2. Portlet
>>>>>>>> 3. Preferenence
>>>>>>>>
>>>>>>>> simple right? a page contains a portlet contains preferences. a
>>>>>>>> delete / update / etc of Page should affect all its portlets,
>>>>>>>> and a delete / update / etc of a portlet should affect its
>>>>>>>> preferences. each of these has a name.
>>>>>>>>
>>>>>>>> to model the containment, Portlet's ID is the page name + it's
>>>>>>>> own name (PortletId). that one is simple. however, when we go a
>>>>>>>> level deeper to Preference, Preference's ID is the Portlet's ID
>>>>>>>> + it's own name (PreferenceId).
>>>>>>>>
>>>>>>>> how do i model that? PortletId is *not* a simple type, so I
>>>>>>>> can't use it as an @Id field in Preference. how do i define
>>>>>>>> Preference's ID fields?
>>>>>>>>
>>>>>>> Id fields:
>>>>>>>
>>>>>>> In page: pageId
>>>>>>> In Portlet: pageId & portletId, IdClass has pageId & portletId
>>>>>>> fields
>>>>>>> In preference: pageId & portletId & preferenceId, IdClass has
>>>>>>> pageId & portletId & preferenceId fields
>>>>>>
>>>>>>
>>>>>>
>>>>>> so the general ID is that each "contained" entity needs to have
>>>>>> simple typed ID fields for every containing entity class.
>>>>>>
>>>>>> this is sort of confusing, because it means that a Preference
>>>>>> needs to know about pages, when ideally all it would care about is
>>>>>> the containing entity above it. but i think i get it. for example,
>>>>>> as long as a Portlet has a unique ID, why does a Preference needs
>>>>>> to know or care about the Page? the Portlet has already
>>>>>> encapsulated the Page ID into it's ID, why does it need to be
>>>>>> exposed to the Preference?
>>>>>>
>>>>>>>
>>>>>>>> when i just make it a @JoinColumn instead,
>>>>>>>>
>>>>>>>> / @ManyToOne(optional = false, cascade = CascadeType.ALL)
>>>>>>>> @Column(insertable=false, updatable=false, nullable=false,
>>>>>>>> name="PORTLET_ID")
>>>>>>>> @XmlTransient
>>>>>>>> private Portlet portlet;
>>>>>>>>
>>>>>>>> /i get this ...
>>>>>>>>
>>>>>>>> /Exception Description: The @JoinColumns on the annotated
>>>>>>>> element [private com.sun.portal.pom.Entity
>>>>>>>> com.sun.portal.pom.Preference.entity] from the entity class
>>>>>>>> [class com.sun.portal.pom.Preference] is incomplete. W*hen the
>>>>>>>> source entity class uses a composite primary key, a @JoinColumn
>>>>>>>> must be specified for each join column using the @JoinColumns.*
>>>>>>>> Both the name and the referenceColumnName elements must be
>>>>>>>> specified in each such @JoinColumn.
>>>>>>>> /
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Looks like you used /_at_JoinColumn*s*. Please check.
>>>>>>>
>>>>>>> /
>>>>>>
>>>>>>
>>>>>>
>>>>>> no, i didn't. the message is telling me that i need to specify >1
>>>>>> @JoinColumn's, and use a @JoinColumns to contain them. i think.
>>>>>>
>>>>>> i eventually got something that appears to work, on TopLink. i had
>>>>>> to remove the @JoinColumn from the @ManyToOne in Preference and
>>>>>> Portlet. this is course means i'm missing insertable and updatable
>>>>>> = false settings.
>>>>>>
>>>>>>>> what am i supposed to use for the join columns? since PortletId
>>>>>>>> encapsulates both the Page name and Portlet name, why does it
>>>>>>>> want to to specify more than that?
>>>>>>>>
>>>>>>>> i feel like i must be making this more complicated than it needs
>>>>>>>> to be. all i want is to model some objects containing other
>>>>>>>> objects where all operations cascade.
>>>>>>>>
>>>>>>> This should be doable. First start with the Page and Portlet
>>>>>>> classes. When this works, add Preferences and model the
>>>>>>> relationships and IdClass accordingly.
>>>>>>>
>>>>>> i know the 2-level case works. the 3 level case is giving me
>>>>>> trouble because i have a composite ID that has (at least
>>>>>> conceptually) another composite ID as a field.
>>>>>>
>>>>>>> -- markus.
>>>>>>>
>>>>>>>> ?
>>>>>>>>
>>>>>>>> Markus Fuchs wrote:
>>>>>>>>
>>>>>>>>> Hi Jeffrey,
>>>>>>>>>
>>>>>>>>> Entities should not use relationship fields as part of the
>>>>>>>>> primary key. The JPA spec says (please see 2.1.4.):
>>>>>>>>>
>>>>>>>>> The primary key (or field or property of a composite primary
>>>>>>>>> key) should be one of the following types:
>>>>>>>>> any Java primitive type; any primitive wrapper type;
>>>>>>>>> java.lang.String; java.util.Date;
>>>>>>>>> java.sql.Date.
>>>>>>>>>
>>>>>>>>> Try changing:
>>>>>>>>>
>>>>>>>>> public abstract class Entity implements Serializable {
>>>>>>>>> @Id
>>>>>>>>> @Column (nullable = false, name="ENTITY_NAME")
>>>>>>>>> @XmlAttribute
>>>>>>>>> private String name;
>>>>>>>>>
>>>>>>>>> @Id
>>>>>>>>> @Column (nullable = false, name="PAGE_ID") private
>>>>>>>>> String pageId;
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> @Column (nullable = false, name="PAGE_ID") @ManyToOne
>>>>>>>>> (cascade = CascadeType.ALL, insertable=false, updatable=false)
>>>>>>>>> private Page page = null;
>>>>>>>>> ...
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> public class EntityId implements Serializable {
>>>>>>>>> private String name;
>>>>>>>>> private String pageId;
>>>>>>>>> ...
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> -- markus.
>>>>>>>>>
>>>>>>>>> Entity
>>>>>>>>> Jeffrey Blattman wrote:
>>>>>>>>>
>>>>>>>>>> hi,
>>>>>>>>>>
>>>>>>>>>> 2.0-b56, i'm seeing this ...
>>>>>>>>>>
>>>>>>>>>> /Exception Description: Invalid composite primary key
>>>>>>>>>> specification. The names of the primary key fields or
>>>>>>>>>> properties in the primary key class
>>>>>>>>>> [com.sun.portal.pom.EntityId] and those of the entity bean
>>>>>>>>>> class [class com.sun.portal.pom.Entity] must correspond and
>>>>>>>>>> their types must be the same. Also, ensure that you have
>>>>>>>>>> specified id elements for the corresponding attributes in XML
>>>>>>>>>> and/or an @Id on the corresponding fields or properties of the
>>>>>>>>>> entity class.
>>>>>>>>>> /
>>>>>>>>>> i've attached the two classes. Entity has IDs /name/ and
>>>>>>>>>> /page/, and EntityId has fields /name/ and /page/. same name,
>>>>>>>>>> same type. i can zip the maven2 project so the problem can be
>>>>>>>>>> reproduced, if that helps.
>>>>>>>>>>
>>>>>>>>>> what am i missing?
>>>>>>>>>>
>>>>>>>>>> thanks.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>
>>