【问题标题】:JPA @OneToOne throws Error when mapped to an abstract @Entity with subclassesJPA @OneToOne 在映射到带有子类的抽象 @Entity 时抛出错误
【发布时间】:2011-07-11 08:32:34
【问题描述】:

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

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

【问题讨论】:

  • 数据库中鉴别器列的数据类型是什么?
  • 你能显示完整的堆栈跟踪吗?
  • 您是否尝试过@MappedSuperclass 而不是Person 上的@Entity?
  • This forum post 可能也有帮助,尽管它是从 2007 年开始的。本质上,它建议向抽象基类添加一个虚拟鉴别器值。
  • @Martin - 我想将这些实体存储在同一张表上,我猜 @MappedSuperclass 不能映射到实体关系上,例如@OneToOne

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


【解决方案1】:

这对我有用,使用 hibernate 作为持久性提供程序。

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

【讨论】:

    【解决方案2】:

    来自The Java EE 6 Tutorial - Entity Inheritence

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

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

    来自@MappedSuperclass javadoc

    用 MappedSuperclass 注解可以 以与实体相同的方式映射 除了映射将适用 由于没有表,仅对其子类 映射的超类存在 自己。

    因此您不能在 Person 上使用 @MappedSuperclass,然后将其与 @OneToOne 映射,因为不会有 Person 表。

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

    【讨论】:

    • 这解决了我的问题,我在子类上忘记了@Entity,并收到同样的错误,直到我将它添加到子类中。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      @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 属性就不再需要了

      【讨论】:

        【解决方案5】:

        正如我在上面的评论中所指出的,我已经尝试过使用 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 中的不同行为。

        【讨论】:

          【解决方案6】:

          我运行与此类似的代码,但有一些不同之处。首先,我将 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”。花了很多时间才让它工作,我希望它能解决你的问题。

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              猜你喜欢
              • 2023-04-07
              • 1970-01-01
              • 1970-01-01
              • 2017-01-10
              • 2014-05-17
              • 2011-05-15
              • 1970-01-01
              • 1970-01-01
              • 2022-10-06
              相关资源
              最近更新 更多