Наследование JPA требует ID в подклассе

У меня проблема с моей моделью домена jpa. Я просто пытаюсь поиграть с простым наследованием, для которого я использую простой базовый класс Person и подкласс Customer. Согласно официальной документации (как JPA, так и EclipseLink) мне нужен только атрибут / столбец ID в базовом классе. Но когда я запускаю свои тесты, я всегда получаю сообщение о том, что у клиента нет @Id?

Сначала я подумал, что проблема заключается в видимости атрибута id, потому что он сначала был частным. Но даже после того, как я изменил его на защищенный (чтобы подкласс имел прямой доступ), он не работает.

Человек:

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

Клиент:

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

    //no id needed here

У меня заканчиваются идеи и ресурсы, на которые можно было бы взглянуть. Это должна быть довольно простая проблема, но я ее просто не вижу.


person lostiniceland    schedule 12.09.2009    source источник
comment
У меня похожая проблема. Я считаю, что это связано с Затмением. Мои инструменты разработчика JBoss жалуются на отсутствие @Id в производном объекте, но Maven компилирует его без жалоб.   -  person Mats    schedule 27.07.2015


Ответы (7)


Я решил это сам, создав MappedSuperclass

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

   ...setter/getter
}

Все сущности наследуются от этого класса. Мне все еще интересно, почему в руководствах об этом не упоминается, но, возможно, это станет лучше с реализациями JPA 2.

person lostiniceland    schedule 14.09.2009
comment
+1. Я считаю, что это правильный ответ на ваш конкретный вопрос. - person Hosam Aly; 12.10.2009
comment
Спасибо за комментарий. Я все еще не был уверен в этом. - person lostiniceland; 13.10.2009
comment
К настоящему времени в oracle-doc упоминается аннотация MappedSuperclass: docs .oracle.com / javaee / 7 / tutorial / doc / persistence-intro002.htm. Это определенно правильный путь :) - person L. Monty; 19.11.2014

У меня была точно такая же проблема. Для подкласса я получал:

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

В моем случае оказалось, что я забыл добавить свой корневой класс в persistence.xml.

Убедитесь, что у вас есть как лицо, так и клиент, определенные в:

<persistence>
  <persistence-unit>
    ...
    <class>package.Person</class>
    <class>package.Customer</class>
    ...
  </persistence-unit>
</persistence>
person tk.luczak    schedule 12.09.2012

JPA знает два разных способа применения наследования:

  • Наследование присоединенной таблицы
  • Наследование одной таблицы

При наследовании одной таблицы вам понадобится столбец дискриминатора, чтобы различать строки в таблице.

При наследовании объединенной таблицы каждый класс получает свою собственную таблицу, поэтому столбец дискриминатора не требуется.

Я думаю, ваша проблема в том, что вы смешали эти два понятия. Так что либо:

  • определить столбцы дискриминатора и использовать InheritanceType.SINGLE_TABLE и не использовать @ Table в подклассах
  • или используйте InheritanceType.JOINED, но не указывайте столбец дискриминатора и значения!
person Tom van Zummeren    schedule 12.09.2009
comment
Пробовал, но проблема та же. Кроме того, согласно en.wikibooks.org/wiki/Java_Persistence/ вы можете использовать дискриминатор - person lostiniceland; 12.09.2009

Я знаю, что это старое, но, тем не менее, действительное. Я столкнулся с той же проблемой. Однако @MappedSuperClass - это не то же самое, что таблица наследования Single. MappedSuperClass создаст отдельные таблицы для каждого из подклассов (насколько я понимаю)

Я не совсем уверен, почему, но когда у меня был только один унаследованный класс, у меня не было проблем. Однако как только я добавил второй и третий, я получил ту же ошибку. Когда я указал аннотацию @Id в дочерней таблице, она снова заработала.

Мой макет был простым, с контактной информацией для компаний, агентов и клиентов.

Родительская таблица:

...
@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;
...

Контакты агента

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

Контактное лицо компании:

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

}

Контакты клиента:

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

Таблица клиентов еще не построена, поэтому информации о сопоставлении нет, но суть вы поняли.

Я хотел поделиться описанием MySQL, но командная строка Windows слишком бесполезна для вырезания / копирования / вставки! По сути, это: ID (int pri) USER_TYPE (VARCHAR (10)) USER_ID (INT) MAPTYPE (VARCHAR (10)) MAPVALUE (VARCHAR (120))

Мне все еще нужно настроить все тесты, но пока все выглядит хорошо (боюсь, что если я подожду, пока не сделаю все тесты, я забуду опубликовать это)

person Alan B. Dee    schedule 28.12.2011

При использовании Glassfish / EclipseLink может быть другая причина этой ошибки: EclipseLink ‹2.6.0 имеет неприятная ошибка, из-за которой классы с лямбда-выражениями игнорировались. Это может привести к сбивающим с толку ошибкам, подобным упомянутой здесь, или другим подобным ошибкам (например, EclipseLink может сообщить вам, что класс с аннотацией @Entity не является сущностью).

Glassfish 4.1 включает EclipseLink 2.5.x, поэтому эта ошибка (и многие многие другие) укусит вас при использовании Glassfish. Я использовал эту версию вместо 4.1.1 из-за другой ошибки, которая сделала невозможным использование проверки в веб-службах REST. Держитесь как можно дальше от Glassfish, если хотите сохранить рассудок.

person José González    schedule 25.02.2016

Вы можете получить этот тип ошибки, если не создаете схему базы данных на основе ваших сущностей. Если это так:

1-й - у вашего суперкласса всегда должен быть @Id

2-й - у вашего подкласса должен быть столбец, определяющий расширенный класс (суперкласс @Id)

3-й - Простейшим решением для представленного выше случая было бы добавление идентификатора столбца в таблицу подкласса с соответствующим ограничением внешнего ключа.

Надеюсь, это кому-то поможет! Пальцы вверх!

person João Rodrigues    schedule 17.10.2016

Поскольку я видел некоторые ответы, которые могут помочь, но не полный, я постараюсь дать один.

Как говорится в принятом ответе, вы должны объявить наследование в базовом классе. Это можно сделать 4 разными способами:

  • @MappedSuperclass

Сопоставит каждый конкретный класс с таблицей

  • @Inheritance (стратегия = InheritanceType.TABLE_PER_CLASS)

Подобно @MappedSuperclass, но суперкласс также является сущностью

  • @ Наследование (стратегия = InheritanceType.SINGLE_TABLE)

Все конкретные классы будут отображены в одной таблице

  • @Inheritance (стратегия = InheritanceType.JOINED)

У каждого класса своя таблица. Объединенные поля будут отображены в таблице для абстрактного суперкласса.

Вы можете узнать больше о стратегиях наследования JPA здесь:
https://www.gotits-on-java.org/complete-guide-inheritance-strategies-jpa-hibernate/


Если структура класса разделена на разные проекты, вам, возможно, придется объявить суперкласс в вашем persistence.xml, как сказал tk.luczak в своем ответе

person stefan    schedule 22.10.2018