Question

I have a problem with my jpa domain model. I am just trying to play around with simple inheritance for which I use a simple Person base-class and and a Customer subclass. According to the official documentation (both, JPA and EclipseLink) I only need the ID-attribute/column in the base-class. But when I run my tests, I always get an error telling me that Customer has no @Id?

First I thought the problem lies in the visibility of the id-attribute, because it was private first. But even after I changed it to protected (so the subclass has direct access) it isnt working.

Person:

@Entity @Table(name="Persons")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "TYPE")
public class Person {

    @Id
    @GeneratedValue
    protected int id;
    @Column(nullable = false)
    protected String firstName;
    @Column(nullable = false)
    protected String lastName;

Customer:

@Entity @Table(name = "Customers")
@DiscriminatorValue("C")
public class Customer extends Person {

    //no id needed here

I am running out of ideas and resources to look at. It should be a rather simple problem, but I just dont see it.

Was it helpful?

Solution

I solved it myself by creating a MappedSuperclass

@MappedSuperclass
public abstract class EntityBase{
   @Id
   @GeneratedValue
   private int id;

   ...setter/getter
}

All entities are inheriting from this class. I am still wondering why the tutorials dont mention this, but maybe it gets better with the JPA 2 implementations.

OTHER TIPS

I had exactly the same problem. For subclass I was getting:

Entity class [...] has no primary key specified. It should define either an @Id, @EmbeddedId or an @IdClass.

In my case it turned out that I forgot to add my root class in persistence.xml.

Make sure that you have both Person and Customer defined in:

<persistence>
  <persistence-unit>
    ...
    <class>package.Person</class>
    <class>package.Customer</class>
    ...
  </persistence-unit>
</persistence>

I know this is old but nonetheless valid. I ran into the same problem. However, the @MappedSuperClass is not the same as a Single inheritance table. The MappedSuperClass will create separate tables for each of the subclasses (as I understand)

I'm not sure exactly why, but when I only have one inherited class I had no problems. However, once I added the second and third then I received the same error. When I specified @Id annotation in the child table it started working again.

My layout was simple, contact information for Companies, Agents, and Clients.

Parent Table:

...
@Entity
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="USER_TYPE", length=10, discriminatorType= DiscriminatorType.STRING)
@Table(name="CONTACTS")
public abstract class AbstractContact implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @Column (length=10, nullable=false)
    protected String mapType;
    @Column (length=120, nullable=false)
    protected String mapValue;
...

Agent Contacts

@Entity
@DiscriminatorValue("Agent")
public class AgentContact extends AbstractContact implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="USER_ID")
    protected Agents agent;
}

Company Contact:

@Entity
@DiscriminatorValue("Company")
public class CompanyContact extends AbstractContact implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="USER_ID")
    protected Companies company;

}

Client Contacts:

@Entity
@DiscriminatorValue("Client")
public class ClientContact extends AbstractContact implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
    //Client table not built yet so... no mapping
}

The clients table isn't built yet so there's no mapping information, but you get the point.

I wanted to share the MySQL description but Windows Command Prompt is too worthless to cut/copy/paste! In essentially, its: ID (int pri) USER_TYPE (VARCHAR(10)) USER_ID (INT) MAPTYPE (VARCHAR(10)) MAPVALUE (VARCHAR(120))

I still have to setup all the tests, but so far it looks good (I fear that if I wait until after I make all the tests I'll forget to post this)

JPA knows two different ways to apply inheritance:

  • Joined table inheritance
  • Single table inheritance

With single table inheritance you will need a discriminator column to distinguish between the rows in the table.

With joined table inheritance, each class gets it's own table so no discriminator column is needed.

I think your problem is that you mixed the two concepts. So either:

  • define the discriminator columns and use `InheritanceType.SINGLE_TABLE` and don't use `@Table` on subclasses
  • or use `InheritanceType.JOINED` but don't specify the discriminator column and values!

There may be another reason for this error when using Glassfish / EclipseLink: EclipseLink <2.6.0 has a nasty bug causing classes with lambda expressions in it to be ignored. That may lead to confusing errors, like the one mentioned here, or other similar errors (for example, EclipseLink may tell you that a class with an @Entity annotation is not an entity).

Glassfish 4.1 includes EclipseLink 2.5.x, so this bug (and many many others) will bite you if using Glassfish. I was using this version instead of 4.1.1 due to another bug that made impossible to use validation in REST web services. Stay as far as possible from Glassfish if you want to keep your sanity.

You might get this type of error when you're not generating the database schema based in your entities. If that is the case:

1st - Your superclass must always have an @Id

2nd - Your subclass must have some column that identifies the extended class (super class @Id)

3rd - Simplest solution for the presented case above would be add a column id to subclass table, with the corresponding foreign key constraint.

Hope this helps someone! Thumbs up!

As i have seen some answers that might help, but not a complete answer, i'll try to provide one.

As the accepted answer states, you have to declare the inheritance in the base class. There are 4 different ways you can do that:

  • @MappedSuperclass

Will map each concrete class to table

  • @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

Similar to @MappedSuperclass but the superclass is also an entity

  • @Inheritance(strategy = InheritanceType.SINGLE_TABLE)

All concrete classes will be mapped by the same table

  • @Inheritance(strategy = InheritanceType.JOINED)

Every class gets its own table. Joined fields will be mapped by the table for the abstract superclass.

You can read more about JPA inheritance strategies here:
https://www.thoughts-on-java.org/complete-guide-inheritance-strategies-jpa-hibernate/


If the class structure is split up over different projects, you might have to declare the superclass in your persistence.xml, as tk.luczak stated in his answer

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top