JPA Наследование требует ID в подклассе - PullRequest
25 голосов
/ 12 сентября 2009

У меня проблема с моей моделью домена 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

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

Ответы [ 7 ]

31 голосов
/ 15 сентября 2009

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

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

   ...setter/getter
}

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

9 голосов
/ 13 сентября 2012

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

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>
4 голосов
/ 28 декабря 2011

Я знаю, что это старый, но тем не менее действительный. Я столкнулся с той же проблемой. Однако @MappedSuperClass не совпадает с таблицей одиночного наследования. 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))

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

3 голосов
/ 12 сентября 2009

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

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

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

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

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

  • определяют столбцы дискриминатора и используют `InheritanceType.SINGLE_TABLE` и не используют` @ Table` на подклассах
  • или используйте `InheritanceType.JOINED`, но не задайте столбец и значения дискриминатора!
0 голосов
/ 22 октября 2018

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

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

  • @ MappedSuperclass

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

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

Аналогично @MappedSuperclass, но суперкласс также является сущностью

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

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

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

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

Подробнее о стратегиях наследования JPA можно прочитать здесь:
https://www.thoughts -on-java.org / полная-гид-наследственная-стратегия-JPA-спящий режим /


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

0 голосов
/ 17 октября 2016

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

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

2nd - Ваш подкласс должен иметь некоторый столбец, который идентифицирует расширенный класс (суперкласс @Id)

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

Надеюсь, это кому-нибудь поможет! Недурно!

0 голосов
/ 25 февраля 2016

При использовании Glassfish / EclipseLink может быть еще одна причина: EclipseLink <2.6.0 содержит <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=429992" rel="nofollow"> неприятную ошибку , приводящую к игнорированию классов с лямбда-выражениями. Это может привести к ошибочным ошибкам, таким как упомянутая здесь, или другим подобным ошибкам (например, EclipseLink может сказать вам, что класс с аннотацией @Entity не является сущностью).

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

...