Why is this JPA 2.0 mapping giving me an error in Eclipse/JBoss Tools?
Asked Answered
P

3

2

I have the following situation: Valid XHTML
(source: kawoolutions.com)

JPA 2.0 mappings (It might probably suffice to consider only the Zip and ZipId classes as this is where the error seems to come from):

@Entity
@Table(name = "GeoAreas")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
public abstract class GeoArea implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    protected Integer id;

    @Column(name = "name")
    protected String name;

    ...
}

@Entity
@Table(name = "Countries")
@DiscriminatorValue(value = "country")
public class Country extends GeoArea
{
    @Column(name = "iso_code")
    private String isoCode;

    @Column(name = "iso_nbr")
    private String isoNbr;

    @Column(name = "dial_code")
    private Integer dialCode = null;

    ...
}

@Entity
@Table(name = "Zips")
@IdClass(value = ZipId.class)
public class Zip implements Serializable
{
    @Id
    @Column(name = "code")
    private String code;

    @Id
    @ManyToOne
    @JoinColumn(name = "country_code", referencedColumnName = "iso_code")
    private Country country = null;

    ...
}

public class ZipId implements Serializable
{
    private String country;

    private String code;

    ...
}

Pretty simple design:

A country is a geo area and inherits the ID from the root class. A ZIP code is unique within its country so it combines an ISO code plus the actual ZIP code as PK. Thus Zips references Countries.iso_code, which has an alternative unique, not-null key on it (reference to non-primary key column!). The Zip.country association gets an @Id annotation and its variable name is the same as the one in its ID class ZipId.

However I get this error message from within Eclipse (also using JBoss Tools):

Validation Message: "The attribute matching the ID class attribute country does not have the correct type java.lang.String"

  1. Why is this wrong in JPA 2.0 syntax (see @Id annotation on Zip.country)? I don't think it is. After all the types of Zip.country and ZipId.country can't be the same for JPA 2 because of the @Id annotation on the @ManyToOne and the PK being a simple integer, which becomes the ID class counterpart. Can anyone check/confirm this please?
  2. Could this be a bug, probably in JBoss Tools? (Which software component is reporting the above bug? When putting the 3 tables and entity classes into a new JavaSE project there's no error shown with the exact code...)
Padriac answered 25/4, 2011 at 23:8 Comment(2)
In Zips country_code is a char(2) whereas in countries the primary key it references is an integer. You need a unique key over iso_code which is the one you seem to reference and fix the foreign key. Isn't so?Train
This is the specialty of the design: Zips has an identifying relationship, that is an FK that's also a PK to a non-primary key column. Zips.country_code references alternative key Countries.iso_code. This should work (other than JPA doesn't allow such references, which I doubt).Padriac
P
2

Answering own question...

The way I modeled the reference, I use a String because the FK points to the iso_code column in the Countries table which is a CHAR(2), so basically my mapping is right. However, the problem is that JPA 2.0 doesn't allow anything but references to primary key columns. This is what the Eclipse Dali JPA validator shows.

Taken from "Pro JPA 2.0" by Keith/Schincariol p.283 top, "Basic Rules for Derived Identifiers" (rule #6): "If an id attribute in an entity is a relationship, then the type of the matching attribute in the id class is of the same type as the primary key type of the target entity in the relationship (whether the primary key type is a simple type, an id class, or an embedded id class)."

Personal addendum:

I disagree with JPA 2.0 having this limitation. JPA 1.0 mappings allow references to non-PK columns. Note, that using JPA 1.0 mappings instead isn't what I'm looking for. I'd rather be interested in the reason why this restriction was imposed on JPA 2.0. The JPA 2.0 is definitely limiting.

Padriac answered 29/4, 2011 at 8:3 Comment(1)
i also use the @IdClass mapping. The mapping works just eclipse reports an error during!Zook
V
1

I'd say focus your attention on the CompoundIdentity relationship. See this question, and my answer there Help Mapping a Composite Foreign Key in JPA 2.0

ZipId has no "country" field in your case

Vanhook answered 28/4, 2011 at 15:9 Comment(7)
ZipId does have a "country" field. I don't exactly get your point. Can you clarify?Padriac
Note the foreign key from the Zips table to Countries itself isn't composite, only the primary key is. The country reference "points to" a single CHAR(2) column, which is nothing more but the well-known ISO country codes (here just an alternative, natural key, because the JPA forces me into using an INTEGER ID for the complete inheritance tree).Padriac
A compound identity (where a FK is aprt of the PK) has to have an IdClass which has fields the same names as the fields of the class. Yours fails that condition. Look at the link. Yes I know you want to link to non-PK cols of the other class but I opine that you can'tVanhook
Did you miss the Zip and ZipId classes in the code box? They're there. I don't get it.Padriac
Didn't miss anything, "country" in ZipId is not the right type.Vanhook
Well, yes and no. The way I modeled the reference, I use a String because the FK points to the iso_code column in the Countries table which is a CHAR(2), so basically my mapping is right. What you mean is that JPA 2.0 doesn't allow anything but references to primary key columns, which is the whole point of this question. I just disagree with JPA 2.0 having this limitation. JPA 1.0 mappings allow references to non-PK columns. Note, that using JPA 1.0 mappings instead isn't what I'm looking for. I'm rather looking for the reason why this restriction was imposed on JPA2.Padriac
Please see my own answer for additional info.Padriac
T
0

I have not tested your code, but it looks pretty much related to the use of the @PrimareKeyJoinColumn annotation.

The JPA 2.0 specification in section 11.1.40 states:

The PrimaryKeyJoinColumn annotation is used to join the primary table of an entity subclass in the JOINED mapping strategy to the primary table of its superclass; it is used within a SecondaryTable annotation to join a secondary table to a primary table; and it may be used in a OneToOne mapping in which the primary key of the referencing entity is used as a foreign key to the referenced entity[108].

The example in the spec looks like your case.

@Entity
@Table(name="CUST")
@Inheritance(strategy=JOINED)
@DiscriminatorValue("CUST")
public class Customer { ... }

@Entity
@Table(name="VCUST")
@DiscriminatorValue("VCUST")
@PrimaryKeyJoinColumn(name="CUST_ID")
public class ValuedCustomer extends Customer { ... }

I hope that helps!

Train answered 25/4, 2011 at 23:43 Comment(2)
Note there's no error occurring in the inheritance hierarchy but on the Zip class and its ID class ZipId, with the former having an association to the Country sub class. There seems to be something why Zip.country and ZipId.country don't map correctly. ThanksPadriac
Note that @PrimaryKeyJoinColumn(name=...) is only necessary if sub tables change the name of the primary key column/s, which is not the case here (both named id).Padriac

© 2022 - 2024 — McMap. All rights reserved.