persistence@glassfish.java.net

Re: "Invalid composite primary key specification"

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

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.

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.

?

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