当映射到具有子类的抽象 @Entity 时,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,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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这对我来说很有效,使用 hibernate 作为持久性提供者。
This did the trick for me, using hibernate as persistence provider.
来自 Java EE 6 教程 - 实体继承:
因此,您似乎是正确的,您必须使用
@Entity
注释Person
类,以通过@OneToOne 将其与
。Location
关联起来来自 @MappedSuperclass javadoc
因此,您不能在 Person 上使用
@MappedSuperclass
,然后将其与@OneToOne
进行映射,因为不会有 Person 表。看来您使用的 JPA 注释是正确的。您是否尝试过 @Martin Klinke 对
Person
类的虚拟鉴别器值的建议?From The Java EE 6 Tutorial - Entity Inheritence:
So you seem to be correct that you have to annotate the
Person
class with@Entity
to associate it with aLocation
via@OneToOne
.From the @MappedSuperclass javadoc
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?正如我上面的评论所示,我已经尝试使用 EclipseLink 进行相同的操作,并且它有效。
创建一些测试数据后,我清除了数据库中人员条目的鉴别器值,现在在尝试加载关联位置时遇到类似的异常。 EclipseLink 的消息更具描述性:
映射似乎有效,因此除非数据“损坏”(正如您所说,这不是您的情况),否则它可能是一个错误,或者至少是 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:
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.
我发现如果实体类实现
Serialized
,这种问题就可以自行解决。I found this kind of problem solves itself if the Entity classes implement
Serializable
.我有一个类似的错误消息,具有以下结构:
具有具体的子实例
和具有引用抽象类的字段的类:
以下更改为我解决了问题:
@OneToOne
更改为 < code>@OneToOne(cascade = CascadeType.ALL)@GenerateValue
更改为@GenerateValue(strategy = GenerationType.TABLE)
更新:我还发现我是在将 RealPerson 对象链接到 SomeClass 之前不保存它。所以现在我先保存实例,就不再需要
cascade
属性了I had a similar error message with the following structure:
with a concrete child instance
and a class that has a field referencing the abstract class:
The following changes fixed the problem for me:
@OneToOne
to@OneToOne(cascade = CascadeType.ALL)
@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我运行与此类似的代码,但有一些差异。首先,我将抽象类放在接口后面。其次,我显式定义了 @OnetoOne 映射的 targetEntity。一个例子是:
为了清楚起见,这里我将您的 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:
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.
尝试将 @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.