persistence@glassfish.java.net

Re: "Invalid composite primary key specification"

From: Jeffrey Blattman <jeffrey.blattman_at_gmail.com>
Date: Mon, 06 Aug 2007 15:52:14 -0700

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.