Jochen,
Using a mapkey with a composite primary key is now supported and has been
for some time. The code below indicates that the version of the code you are
running supports this functionality, however if possible you should run
against the latest version of Glassfish and if the problem persists, please
enter a bug.
Cheers,
Guy
----- Original Message -----
From: "Jochen Seifarth" <js_at_agentbob.info>
To: <persistence_at_glassfish.dev.java.net>
Sent: Tuesday, February 14, 2006 12:00 PM
Subject: RE: Foreign key as part of compound key and one-to-many
relationship
> Sorry for wasting your time......sometimes it helps to read the source ;-)
>
> Currently, Toplink in Glassfish just doesn't support using @MapKey for
> composite keys, regardless if the composite primary key is specified via
> @IdClass or @EmbeddedId. So what I was trying to achieve is simply not
> supported yet. Any idea anybody when this will be supported ?
>
> http://fisheye5.cenqua.com/viewrep/glassfish/entity-persistence/src/java/oracle/toplink/essentials/internal/annotations/EJBAnnotationsProcessor.java?r=1.14
> which is the most recent version:
>
> 1592 if (name.equals("") && referenceDmd.hasCompositePrimaryKey()) {
> 1593 // No persistent property or field name has been provided, and
> 1594 // the reference class has a composite primary key class. Let
> 1595 // it fall through to return null for the map key. Internally,
> 1596 // TopLink will use an instance of the composite primary key
> 1597 // class as the map key.
> 1598 } else {
> 1599 // A persistent property or field name may have have been
> 1600 // provided. If one has not we will default to the primary
> 1601 // key of the reference class. The primary key cannot be
> 1602 // composite at this point.
> 1603 String fieldOrPropertyName = getName(name,
> referenceDmd.getIdAttributeName(),
> EJBAnnotationsLogger.MAP_KEY_ATTRIBUTE_NAME, accessor);
>
>
> On Di, Februar 14, 2006 16:25, Jochen Seifarth wrote:
>> Thanks for your reply Gordon,
>>
>> my question really is how do I change my code to use
>> @EmbeddedId AddressPK
>> instead of @IdClass(AddressPK) and 2 @Id's for the two key elements ?
>> Of course, I would like to maintain the relationship to Person.
>> Is that feasible ?
>>
>> The code as I posted it deploys fine but I keep getting complaints about
>> multiple writable instances of one key element (ADDRESS.GID) when trying
>> to use @EmbeddedId - even though I do specify (insertable=false,
>> updatable=false).
>> (I thought b32f was pretty recent as it was released in early Feb or
>> should I rather use b36 ?)
>>
>> The reason I would like to use @EmbeddedId is that I would like to use a
>> Map<AddressPK,Address> in Person (instead of a Collection<Address>) to
>> hold the addresses, so I could use @MapKey<AddressPK> on the Person
>> side.....
>> or could I still use it even though AddressPK is not declared as
>> @EmbeddedId ?
>>
>> Unfortunaly the example you refer to also uses @IdClass and mupltiple
>> @Id's rather than @EmbeddedId....
>>
>>
>> On Di, Februar 14, 2006 16:04, Gordon Yorke wrote:
>>> This may be an issue with using an older Glassfish build. The classes
>>> you
>>> have attached below should deploy successfully.
>>>
>>> In the Glassfish entity-persistence-tests module
>>> oracle/toplink/essentials/testing/models/cmp3/advanced package you can
>>> see
>>> an example similar to yours using Employee and PhoneNumber where
>>> PhoneNumber has a compound Primary Key a component of which is a foreign
>>> key to Employee.
>>>
>>> --Gordon
>>>
>>> -----Original Message-----
>>> From: Jochen Seifarth [mailto:js_at_agentbob.info]
>>> Sent: Tuesday, February 14, 2006 8:01 AM
>>> To: persistence_at_glassfish.dev.java.net
>>> Subject: Foreign key as part of compound key and one-to-many
>>> relationship
>>>
>>>
>>> (Apologies as this is a cross-post from the forum but this seems to be
>>> the
>>> more appropriate place.)
>>>
>>> Can anybody help with my problem described below ? Is this due to:
>>> a) my ignorance being new to EJB persistence
>>> b) a limitation of JSR-220
>>> c) a problem in the Glassfish build (b32f) I am using
>>>
>>> Trying out EJB3 persistence on Glassfish I have come across the
>>> following
>>> problem:
>>>
>>> In a one-to-many relationship, one Person with many Addresses (e.g.
>>> home,
>>> work, etc.), I would like to use the primary key of Person (gid) as part
>>> of the compound key for Address (gid, aid).
>>> So, I define an @Embeddable AddressPK class to hold the compound key
>>> (gid,
>>> aid), use this class as @IdClass on @Entity Address and define @Id gid
>>> and
>>> @Id aid.
>>> So far so good, however, according to my understanding of the spec (=JSR
>>> 220, proposed final draft) I should also be able to specify @EmbeddedId
>>> AddressPK on @Entity Address, which to my mind is cleaner as it avoids
>>> duplication of columns where the names have to match. Using @EmbeddedId
>>> AddressPK works fine as long as I am not trying to map @ManyToOne to
>>> Person.
>>> I tried various approaches:
>>> -Mapping @ManyToOne from Address, specifying to @JoinColumn(name="GID"):
>>> Glassfish (actually Oracle Toplink) keeps complaining about multiple
>>> writeable instances of GID, even though I had set them to
>>> (insertable=false, updatable=false), both in @Column and @JoinColumn, in
>>> either and in various other combinations....
>>> -Trying to map @ManyToOne from AddressPK fails as Toplink complains that
>>> AddressPK is not an entity....
>>>
>>> OK, maybe the spec just doesn't cater for such cases as it refers to
>>> such
>>> compound keys as coming from 'legacy' systems several times. On the
>>> other
>>> hand, from a database perspective there are pretty good reasons for such
>>> keys and relationships. Also, from a business point of view compound
>>> keys
>>> make a lot of sense.
>>>
>>> The next thing I was trying to archieve is to hold the Addresses in
>>> Person
>>> in a Map<AddressPK,Address>, rather than in a Collection<Address> to
>>> allow
>>> me to access items directly by key rather than having to search the
>>> whole
>>> Collection for it. @MapKey should achieve this.... but if I cannot get
>>> AddressPK to be the @EmbeddedId for Address, what would @MapKey use as
>>> the
>>> key ? .....
>>>
>>> Here is my code - I would really appreciate if anybody could show me how
>>> to change it to use AddressPK as @EmbeddedId while maintaining the
>>> bidirectional relationship with Person.
>>>
>>>
>>> @Embeddable
>>> public class AddressPK implements Serializable {
>>> private String gid;
>>> private String aid;
>>>
>>> public AddressPK() {
>>> this("", "");
>>> }
>>>
>>> public AddressPK(String gid, String aid) {
>>> this.gid = gid;
>>> this.aid = aid;
>>> }
>>>
>>> public boolean equals(AddressPK anotherPK) {
>>> return (this.gid.equals(anotherPK.gid) && this.aid
>>> .equals(anotherPK.aid));
>>> }
>>>
>>> public int hashCode() {
>>> return gid.concat(aid).hashCode();
>>> }
>>>
>>> public String getGid() {
>>> return gid;
>>> }
>>> public void setGid(String gid) {
>>> this.gid = gid;
>>> }
>>>
>>> public String getAid() {
>>> return aid;
>>> }
>>>
>>> public void setAid(String aid) {
>>> this.aid = aid;
>>> }
>>> }
>>>
>>> ----------------------------------------------------------
>>>
>>> @Entity
>>> @IdClass(AddressPK.class)
>>> public class Address implements Serializable {
>>> private AddressPK pk;
>>> private Person person;
>>> private String street;
>>>
>>> public Address() {
>>> this(new AddressPK());
>>> }
>>>
>>> public Address(AddressPK pk) {
>>> this.pk = pk;
>>> street = "";
>>> }
>>>
>>> public boolean equals(Address another) {
>>> return (this.pk.equals(another.pk));
>>> }
>>>
>>> public int hashCode(){
>>> return pk.hashCode();
>>> }
>>>
>>> @Id
>>> @Column(insertable=false,updatable=false)
>>> public String getGid() {
>>> return pk.getGid();
>>> }
>>>
>>> public void setGid(String gid) {
>>> this.pk.setGid(gid);
>>> }
>>>
>>> @Id
>>> public String getAid() {
>>> return pk.getAid();
>>> }
>>>
>>> public void setAid(String aid) {
>>> this.pk.setAid(aid);
>>> }
>>>
>>> @ManyToOne
>>> @JoinColumn(name="GID")
>>> public Person getPerson() {
>>> return person;
>>> }
>>>
>>> public void setPerson(Person person) {
>>> this.person = person;
>>> this.pk.setGid(person.getGid());
>>> }
>>>
>>> public String getStreet() {
>>> return street;
>>> }
>>>
>>> public void setStreet(String street) {
>>> this.street = street;
>>> }
>>>
>>> @Transient
>>> public AddressPK getPk() {
>>> return pk;
>>> }
>>> }
>>>
>>> -----------------------------------------------------------
>>>
>>> @Entity
>>> public class Person implements Serializable {
>>> private String gid;
>>> private String name;
>>> private Collection<Address> addresses;
>>>
>>> public Person() {
>>> this("");
>>> }
>>>
>>> public Person(String gid) {
>>> this.gid = gid;
>>> }
>>>
>>> @Id
>>> public String getGid() {
>>> return gid;
>>> }
>>>
>>> public void setGid(String gid) {
>>> this.gid = gid;
>>> }
>>>
>>> @OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
>>> public Collection<Address> getAddresses() {
>>> return addresses;
>>> }
>>>
>>> public void setAddresses(Collection<Address> addresses) {
>>> this.addresses = addresses;
>>> }
>>>
>>> public String getName() {
>>> return name;
>>> }
>>>
>>> public void setName(String name) {
>>> this.name = name;
>>> }
>>> }
>>>
>>>
>>>
>>>
>>>
>>>
>>
>>
>
>