33

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.

lostiniceland
  • 3,729
  • 6
  • 38
  • 50
  • I have a similar problem. I believe it is Eclipse bound. My JBoss Developer Tools complains about the missing @Id on a derived entity but Maven compiles it without complaining. – Mats Jul 27 '15 at 10:21

7 Answers7

41

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.

lostiniceland
  • 3,729
  • 6
  • 38
  • 50
  • 3
    +1. I believe this is the correct answer to your specific question. – Hosam Aly Oct 12 '09 at 13:45
  • Thanks for the comment. I still wasnt sure about that. – lostiniceland Oct 13 '09 at 10:22
  • 2
    By now the oracle-doc does mention the "MappedSuperclass"-annotation : https://docs.oracle.com/javaee/7/tutorial/doc/persistence-intro002.htm . It is definitly the right way :) – L. Monty Nov 19 '14 at 10:07
  • according to baeldung.com/hibernate-inheritance MappedSuperclass – the parent classes, can't be entities. it means you can not put it on Person class. – Amir May 26 '23 at 18:35
10

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>
tk.luczak
  • 111
  • 1
  • 3
4

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!
Tom van Zummeren
  • 9,130
  • 12
  • 52
  • 63
  • 1
    I tried it, but still the same problem. Also, according to http://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Joined.2C_Multiple_Table_Inheritance you can use the discriminator – lostiniceland Sep 12 '09 at 17:18
4

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)

Alan B. Dee
  • 5,490
  • 4
  • 34
  • 29
1

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!

João Rodrigues
  • 766
  • 8
  • 12
1

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

stefan
  • 11
  • 3
0

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.

José González
  • 1,207
  • 12
  • 25