当映射到具有子类的抽象@Entity时,JPA @OneToOne抛出Error

时间:2011-03-08 19:01:00

标签: java hibernate jpa entitymanager one-to-one

当实体被映射到另一个在其子类上有直接实现的实体时,我遇到了问题。请参阅下面的示例映射:

@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 个答案:

答案 0 :(得分:4)

这对我来说很有把握,使用hibernate作为持久性提供程序。

@OneToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 

答案 1 :(得分:2)

来自The Java EE 6 Tutorial - Entity Inheritence

  

任何映射或关系   非实体超类中的注释   被忽略了。

所以,您似乎是正确的,您必须使用Person注释@Entity班级,并通过Location将其与@OneToOne相关联。

来自@MappedSuperclass javadoc

  

指定的班级   MappedSuperclass注释可以   以与实体相同的方式映射   除了映射将适用   因为没有表,只有它的子类   存在于映射的超类   本身。

所以你不能在Person上使用@MappedSuperclass,然后用@OneToOne映射它,因为没有Person表。

似乎您正在使用的JPA注释是正确的。您是否尝试过@Martin Klinke建议Person类的伪鉴别器值?

答案 2 :(得分:1)

如果Entity类实现Serializable,我发现这种问题会自行解决。

答案 3 :(得分:1)

我有类似的错误消息,其结构如下:

@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)
  3. 更新:我还发现在将它链接到SomeClass之前我没有保存RealPerson对象。所以现在我首先保存实例,不再需要cascade属性

答案 4 :(得分:0)

正如我在上面的评论中指出的那样,我已经尝试过与EclipseLink相同的功能。

创建一些测试数据后,我已经清除了DB中人员条目的鉴别器值,现在我在尝试加载相关位置时遇到了类似的异常。 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中的一个错误或至少是不同的行为。

答案 5 :(得分:0)

我运行类似的代码,但有一些区别。首先,我将Abstract类放在接口后面。其次,我明确定义了@OnetoOne映射的targetEntity。一个例子是:

@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'。为了让它发挥作用,我们采取了相当多的努力,我希望它能解决你的问题。

答案 6 :(得分:0)

尝试将@ForceDiscriminator添加到Person。如果没有这个注释,Hibernate经常会尝试实例化父类,忽略应该告诉它实例化哪个子类的鉴别器。