persistence@glassfish.java.net

Re: "Invalid composite primary key specification"

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

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.