persistence@glassfish.java.net

Re: "Invalid composite primary key specification"

From: Jeffrey Blattman <jeffrey.blattman_at_gmail.com>
Date: Tue, 07 Aug 2007 09:42:55 -0700

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