JPA @OneToOne выбрасывает ошибку при сопоставлении с абстрактным @Entity с подклассами - PullRequest
11 голосов
/ 08 марта 2011

У меня проблема, когда сущность сопоставляется с другой сущностью, которая имеет прямую реализацию на своих подклассах.См. Пример сопоставления ниже:

@Entity
class Location {
      @OneToOne
      @JoinColumn(...)
      private Person person;
}

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="person_type",discriminatorType=DiscriminatorType.STRING)
abstract class Person {
}

@Entity
@DiscriminatorValue("M")
class Man extends Person {
    ...
}

@Entity
@DiscriminatorValue("W")
class Woman extends Person {
    ...
}

Теперь вот что у меня есть в таблице базы данных:

таблица местоположений: id = 1, person_id = 1 таблица людей: id = 1, person_type = "M"

Когда я получаю Местоположение с помощью диспетчера сущностей, hibernate генерирует исключение, говорящее о том, что я не могу создать экземпляр абстрактного класса или интерфейса.

Location location = entityManager.find(Location.class, 1L);

Hibernate генерирует эту ошибку:

javax.persistence.PersistenceException: org.hibernate.InstantiationException: Cannot instantiate abstract class or interface: Person
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:630)
at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:195)
at ......
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runTestMethod(UnitilsJUnit4TestClassRunner.java:174)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runBeforesThenTestThenAfters(UnitilsJUnit4TestClassRunner.java:156)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
at org.unitils.UnitilsJUnit4TestClassRunner.invokeTestMethod(UnitilsJUnit4TestClassRunner.java:95)
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
at org.unitils.UnitilsJUnit4TestClassRunner.access$000(UnitilsJUnit4TestClassRunner.java:44)
at org.unitils.UnitilsJUnit4TestClassRunner$1.run(UnitilsJUnit4TestClassRunner.java:62)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
at org.unitils.UnitilsJUnit4TestClassRunner.run(UnitilsJUnit4TestClassRunner.java:68)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Ответы [ 7 ]

4 голосов
/ 28 сентября 2012

Это помогло мне, используя hibernate в качестве провайдера персистентности.

@OneToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 
2 голосов
/ 09 марта 2011

С Учебное пособие по Java EE 6 - Наследование сущностей :

Любые аннотации сопоставлений или отношений в суперклассах, не связанных с сущностями, игнорируются.

Таким образом, вы, похоже, правы, что вам нужно аннотировать класс Person с @Entity, чтобы связать его с Location через @OneToOne.

Из javadoc @ MappedSuperclass

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

Таким образом, вы не можете использовать @MappedSuperclass для Person, затем сопоставьте его с @OneToOne, так как не будет таблицы Person.

Похоже, что используемые вами аннотации JPA верны,Вы пробовали @Martin Klinke предложить фиктивное значение дискриминатора для класса Person?

1 голос
/ 01 марта 2012

У меня было похожее сообщение об ошибке со следующей структурой:

@Entity
@Table(name = "PERSON")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class Person {

  @Id
  @GeneratedValue
  private Long     id;

}

с конкретным дочерним экземпляром

@Entity
@DiscriminatorValue("REALPERSON")
public class RealPerson extends Person{ etc... }

и классом, имеющим поле, ссылающееся на абстрактный класс:

public class SomeClass() {
  @OneToOne
  private Person person;
}

Следующие изменения устранили проблему для меня:

  1. Измените @OneToOne на @OneToOne(cascade = CascadeType.ALL)
  2. Измените @GeneratedValue на @GeneratedValue(strategy = GenerationType.TABLE)

ОБНОВЛЕНИЕ: Я также обнаружил, что не сохранял объект RealPerson до его привязки к SomeClass.Теперь, когда я сначала сохраняю экземпляр, атрибут cascade больше не требуется

1 голос
/ 10 марта 2011

Я обнаружил, что проблема такого рода решается сама собой, если классы Entity реализуют Serializable.

0 голосов
/ 13 июня 2011

Попробуйте добавить @ForceDiscriminator в Person. Без этой аннотации Hibernate часто пытается создать экземпляр родительского класса, игнорируя дискриминатор, который должен сообщить ему, какой дочерний класс создать.

0 голосов
/ 29 марта 2011

Я использую код, похожий на этот, с несколькими отличиями.Во-первых, я размещаю класс Abstract за интерфейсом.Во-вторых, я явно определяю targetEntity для отображения @OnetoOne.Примером может быть:

@Entity
class Location {
    @OneToOne(targetEntity = AbstractPerson.class)
    @JoinColumn(...)
    private Person person;
}

public interface Person {
}

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="person_type",discriminatorType=DiscriminatorType.STRING)
abstract class AbstractPerson implements Person {
}

Здесь я переименовал ваш абстрактный класс Person в «AbstractPerson» для ясности.Надо было немного поиграться, чтобы заставить его работать, надеюсь, это решит вашу проблему.

0 голосов
/ 09 марта 2011

Как указано в моем комментарии выше, я попробовал то же самое с EclipseLink, и он работает.

После создания некоторых тестовых данных я очистил значение дискриминатора записи о человеке в БД, и теперь я получаю аналогичное исключение при попытке загрузить соответствующее местоположение.Сообщение EclipseLink немного более наглядно:

Exception Description: Missing class for indicator field value [] of type [class java.lang.String].
Descriptor: RelationalDescriptor(com.mklinke.webtest.domain.Person --> [DatabaseTable(PERSON)])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassForIndicatorFieldValue(DescriptorException.java:937)
at org.eclipse.persistence.descriptors.InheritancePolicy.classFromValue(InheritancePolicy.java:355)
at org.eclipse.persistence.descriptors.InheritancePolicy.classFromRow(InheritancePolicy.java:342)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:485)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:456)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:723)
at org.eclipse.persistence.queries.ReadObjectQuery.registerResultInUnitOfWork(ReadObjectQuery.java:766)
at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:451)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1080)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:808)
...

Отображение работает, поэтому, если данные не «повреждены» (чего, как вы сказали, нет), это, вероятно, ошибкаили хотя бы другое поведение в Hibernate.

...