有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java JPA@OneToOne在映射到具有子类的抽象@实体时抛出错误

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

@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,个人id=1 人员表: id=1,person\u 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)

共 (6) 个答案

  1. # 1 楼答案

    正如我在上面的评论中所指出的,我在EclipseLink上也尝试过同样的方法,而且效果很好

    在创建了一些测试数据之后,我清除了数据库中person条目的鉴别器值,现在我在尝试加载相关位置时遇到了类似的异常。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)
    ...
    

    映射似乎是有效的,因此,除非数据“损坏”(正如您所说,在您的情况下并非如此),否则它可能是一个bug,或者至少是Hibernate中的不同行为

  2. # 2 楼答案

    我运行了与此类似的代码,但有几个不同之处。首先,我将抽象类放在接口后面。其次,我明确定义了@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”。花了不少时间才让它工作起来,我希望它能解决你的问题

  3. # 3 楼答案

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

  4. # 4 楼答案

    这对我来说很管用,使用hibernate作为持久性提供者

    @OneToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 
    
  5. # 5 楼答案

    我收到了一条类似的错误消息,其结构如下:

    @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对象链接到某个类之前,我没有保存它。现在我首先保存实例,不再需要cascade属性

  6. # 6 楼答案

    The Java EE 6 Tutorial - Entity Inheritence

    Any mapping or relationship annotations in non-entity superclasses are ignored.

    因此,您必须用@Entity注释Person类,以便通过@OneToOne将其与Location关联,这似乎是正确的

    来自@MappedSuperclass javadoc

    A class designated with the MappedSuperclass annotation can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself.

    所以不能在Person上使用@MappedSuperclass,然后将其映射到@OneToOne,因为将没有Person表

    看起来你使用的JPA注释是正确的。您是否尝试过@Martin Klinge为Person类提供一个虚拟鉴别器值的建议