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

发布于 2024-10-21 01:31:40 字数 3264 浏览 4 评论 0原文

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

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

I have a problem when an Entity is mapped to another Entity which has a direct implementation on its subclasses. See sample mapping below:

@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 {
    ...
}

Now, this is what I have on my database table:

location-table:
id=1, person_id=1
person-table:
id=1,person_type="M"

When I retrieve the Location using entity manager, hibernate throws an exception saying that i cannot instantiate an abstract class or an interface.

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

Hibernate throws this error:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

烟雨扶苏 2024-10-28 01:31:40

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

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

This did the trick for me, using hibernate as persistence provider.

@OneToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 
挽清梦 2024-10-28 01:31:40

来自 Java EE 6 教程 - 实体继承

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

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

来自 @MappedSuperclass javadoc

指定的类
MappedSuperclass注释可以是
以与实体相同的方式映射
除了映射将适用
由于没有表,因此仅适用于其子类
存在于映射的超类中
本身。

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

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

From The Java EE 6 Tutorial - Entity Inheritence:

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

So you seem to be correct that you have to annotate the Person class with @Entity to associate it with a Location via @OneToOne.

From the @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.

So you couldn't use @MappedSuperclass on Person, then map it with the @OneToOne, since there would be no Person table.

Seems like the JPA annotations you are using are correct. Have you tried @Martin Klinke's suggestion of a dummy discriminator value for the Person class?

盛装女皇 2024-10-28 01:31:40

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

As indicated in my comment above, I have tried the same with EclipseLink and it works.

After creating some test data, I've cleared the discriminator value of a person entry in the DB and now I get a similar exception when trying to load the associated location. EclipseLink's message is a little bit more descriptive:

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

The mapping seems to work, so unless the data is "corrupt" (which it isn't in your case as you said), it's probably a bug or at least different behavior in Hibernate.

溺孤伤于心 2024-10-28 01:31:40

我发现如果实体类实现Serialized,这种问题就可以自行解决。

I found this kind of problem solves itself if the Entity classes implement Serializable.

柒七 2024-10-28 01:31:40

我有一个类似的错误消息,具有以下结构:

@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 更改为 < code>@OneToOne(cascade = CascadeType.ALL)
  2. @GenerateValue 更改为 @GenerateValue(strategy = GenerationType.TABLE)

更新:我还发现我是在将 RealPerson 对象链接到 SomeClass 之前不保存它。所以现在我先保存实例,就不再需要 cascade 属性了

I had a similar error message with the following structure:

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

  @Id
  @GeneratedValue
  private Long     id;

}

with a concrete child instance

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

and a class that has a field referencing the abstract class:

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

The following changes fixed the problem for me:

  1. Change the @OneToOne to @OneToOne(cascade = CascadeType.ALL)
  2. Change the @GeneratedValue to @GeneratedValue(strategy = GenerationType.TABLE)

UPDATE: I also found I was not saving the RealPerson object before linking it to SomeClass. So now that I first save the instance first, the cascade attribute is no longer required

娇纵 2024-10-28 01:31:40

我运行与此类似的代码,但有一些差异。首先,我将抽象类放在接口后面。其次,我显式定义了 @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”。花了相当多的时间来让它工作,我希望它能解决你的问题。

I run similar code to this, with a couple of differences. Firstly, I place the Abstract class behind an interface. Secondly, I explicitly define the targetEntity for the @OnetoOne mapping. An example would be:

@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 {
}

Here I've renamed your Person abstract class to 'AbstractPerson' for clarity. Took quite a bit of playing around with to get it working, I hope it solves your issue.

猫卆 2024-10-28 01:31:40

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

Try adding @ForceDiscriminator to Person. Without this annotation Hibernate often tries to instantiate the parent class, ignoring the discriminator that should tell it which child class to instantiate.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文