persistence@glassfish.java.net

RE: Foreign key as part of compound key and one-to-many relationship

From: Jochen Seifarth <js_at_agentbob.info>
Date: Tue, 14 Feb 2006 18:00:35 +0100 (CET)

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;
>> }
>> }
>>
>>
>>
>>
>>
>>
>
>