user1655719
user1655719

Reputation:

JPA : OpenJPA : The id class specified by type does not match the primary key fields of the class

I have includes the error that I am getting while building JPA classes to the table schema shown...can anyone guide me on how to solve this error. And also how to make composite keys when one of the key is actually a foreign key. Whats wrong in my annotations ?

Error

 org.apache.openjpa.util.MetaDataException: The id class specified by type "class Specialty" does not match the primary key fields of the class. 
Make sure your identity class has the same primary keys as your persistent type, including pk field types. Mismatched property: "personId"

Table Schema

create table Location(
  id int primary key,
  city varchar(255),
  state varchar(100),
  country varchar(255)
);
create table Person(
  id int primary key,
  name varchar(100)
);
create table Photographer(
  id int primary key references Person(id) on update cascade on delete cascade,
  livesIn int not null references Location(id) on update cascade on delete no action
);
create table Specialty(
  photographer int references Photographer(id) on update cascade on delete cascade,
  type enum('portrait','landscape','sport'),
  primary key(photographer, type)
);
create table Photo(
  id int primary key,
  takenAt timestamp not null,
  takenBy int references Photographer(id) on update cascade on delete no action,
  photographedAt int references Location(id) on update cascade on delete no action,
  type enum('portrait','landscape','sport')
);
create table Appearance(
  shows int references Person(id) on update cascade on delete cascade,
  isShownIn int references Photo(id) on update cascade on delete cascade,
  primary key(shows, isShownIn)
);

Classes

Person.java

@Entity
public class Person implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;


Location.java

@Entity
public class Location implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String city;

    private String state;

    private String country;


Photographer.java

@Entity
public class Photographer implements Serializable {

    @Id
    @ManyToOne
    @Column(name = "id")
    private Person personId;

    @ManyToOne
    @Column(name = "livesIn")
    private Location livesIn;

Photo.java

@Entity
public class Photo implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    // Foreign Key
    @ManyToOne
    @JoinColumn(name = "takenBy")
    private Photographer takenBy;

    // Foreign Key
    @ManyToOne
    @JoinColumn(name = "photographedAt")
    private Location photographedAt;

    @Basic(optional = false)
    @Column(name = "takenAt", insertable = false, updatable = false)
    @Temporal(TemporalType.DATE)
    private Date takenAt;

    @Enumerated(EnumType.STRING)
    private PhotoType type;

PhotoType

 
public enum PhotoType {
    PORTRAIT("portrait"), LANDSCAPE("landscape"), SPORT("sport");
    private String type;

    PhotoType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

}

Specialty.java

 
import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

@Entity
public class Specialty implements Serializable {

    @EmbeddedId
    protected SpecialtyPK specialtyPK;

    public Specialty() {
        super();
    }

    public Specialty(SpecialtyPK specialtyPK) {
        super();
        this.specialtyPK = specialtyPK;
    }

}

@Embeddable
class SpecialtyPK implements Serializable {

    @ManyToOne
    @Column(name = "id")
    private Photographer personId;

    @Enumerated(EnumType.STRING)
    private PhotoType type;

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((personId == null) ? 0 : personId.hashCode());
        result = prime * result + ((type == null) ? 0 : type.hashCode());
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        SpecialtyPK other = (SpecialtyPK) obj;
        if (personId == null) {
            if (other.personId != null)
                return false;
        } else if (!personId.equals(other.personId))
            return false;
        if (type != other.type)
            return false;
        return true;
    }

    public SpecialtyPK() {
        super();
        // TODO Auto-generated constructor stub
    }

    public SpecialtyPK(Photographer personId, PhotoType type) {
        super();
        this.personId = personId;
        this.type = type;
    }

}

Try with Composite key also failed and gave the same error.

Alot of internet search does not lead to any solution yet.

Upvotes: 4

Views: 5810

Answers (2)

Chris
Chris

Reputation: 21145

The problem is 2 fold. One problem is with personId mapping within SpecialtyPK. @Column is used for basic mappings; you need to specify the column to use for joining using the @JoinColumn annotation.

The second and more important is that primary key classes should not contain relationships - it doesn't matter if it is embeddable defined as a primary key class, it should only contain basic mappings or other primary keys (derrived IDs are allows in JPA 2.0, which means key classes can be nested). So you will need a SpecialtyPK class that has personId with a type of 'int'. You can then specify a ManyToOne mapping within Specialty and add the @MapsId to point it at the int, so that you only need to set the relationship and not manage the basic mapping within the embeddable.

@Entity
public class Specialty implements Serializable {

    @EmbeddedId
    protected SpecialtyPK specialtyPK;

    @MapsId("personId")
    protected Photographer photographer;
..

class SpecialtyPK implements Serializable {

    @Column(name = "id")
    private int personId;//defaults to basic

..

or you can just remove the embeddedId and mark the photographer as the id.

Upvotes: 0

hd1
hd1

Reputation: 34657

Looks to me like you have a composite primary key but your telling JPA, through annotations that you have an embedded primary key. Like I tell everyone who is coming to a project for the first time:

Get the simplest thing working first, without giving heed to efficiency and optimise as client needs dictate.

Premature optimisation is the root of many-a-software problem.

Upvotes: 1

Related Questions