JPA OneToMany 列表没有找到应该继承的mappedBy 属性

Chr*_*ian 5 java inheritance hibernate jpa one-to-many

我们目前正在处理一些要求,我们必须将一些类似的实体(汽车图片、宠物图片、假日图片等)添加到数据库中,这些实体都属于一个所有者(人)。我们不直接链接字节数组,而是使用引用。稍后可能会添加更多图片类型,以便降低复杂性,我们希望将其直接链接到 Java 类,以便我们可以使用instance of类似的东西。我们想使用@Inheritance一个PictureRef包含公共属性并链接到人的超类。然后还有另一个实体Person将包含这些子类的列表。这是OneToManymappedBy属性的关系。此mappedBy属性未知,因此 JPA 向我们返回此错误:

Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown
target entity property:  de.company.project.somepackages.PictureRef.person 
in de.company.project.somepackages.Person.picturesOfCars
Run Code Online (Sandbox Code Playgroud)

我认为最好用下面的代码来说明。为了可读性,我删除了其他属性和 getter/setter 和 id 序列。

1) 所有 JPA 实体都源自一个抽象实体,该实体包含 id 和审计值。这可以正确地与其他子类一起使用,所以我认为这个类不会导致问题。

AbstractEntity

@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
@Id
// Sequence definition removed
private Long id;
// other values are following //
}
Run Code Online (Sandbox Code Playgroud)

2)然后我们有一个必须包含公共属性的超类。所有派生类都必须写在一张表中(因为它们看起来非常相似)。此外,我们还将拥有直接与该实体一起使用的业务逻辑,例如通过 id 加载或删除。

PictureRef

@Entity
@Table(name = "t_picture_ref")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "picture_type")
// Sequence Definition removed
public class PictureRef extends AbstractEntity {
// common attributes, e.g. name or link to file //

@ManyToOne
@JoinColumn(name = "person_id")
private Person person;
}
Run Code Online (Sandbox Code Playgroud)

3) 那么至少有两个子类。更多即将推出。它们将包含仅与此类图片相关的属性。

CarPictureRef

@Entity
@DiscriminatorValue("car")
public class CarPictureRef extends PictureRef {

@Column(name="licence_plate_visible")
private boolean licensePlateVisible;
}
Run Code Online (Sandbox Code Playgroud)

课堂假期PictureRef

@Entity
@DiscriminatorValue("holiday")
public class HolidayPictureRef extends PictureRef {

@Column(name="weather_condition")
private String weatherCondition;
}
Run Code Online (Sandbox Code Playgroud)

4)然后是拥有/上传所有这些图片的人。该人对每种图片都有一个列表,因为应用程序对它们的处理方式完全不同。每个列表都包含具体的子类,但对于mappedBy属性,我们使用person来自超类的PictureRef。也许这种继承是不可能的?

班级人物

@Entity
@Table(name = "t_person")
// Sequence Definition removed
public class Person extends AbstractEntity {

@OneToMany(mappedBy = "person", targetEntity = CarPictureRef.class)
private List<CarPictureRef> picturesOfCars;

@OneToMany(mappedBy = "person", targetEntity = HolidayPictureRef.class)
private List< HolidayPictureRef> picturesOfHolidays;

// a lot of other fields following //
}
Run Code Online (Sandbox Code Playgroud)

一种解决方法可能是仅将具有所有属性的所有内容都存储在一个表中(无论如何我们都想这样做),然后也仅存储在一个 Entity 中PictureRef。然后,我们将在后端编写应用程序逻辑,该逻辑评估pictureType并为相应的业务案例创建新类。但这似乎有点难看 - 我希望有一个针对这个常见用例的 JPA 解决方案?也许我们只是缺少一个或更多的注释?


为了完整起见,我添加了完整的堆栈跟踪。我们正在使用 Hibernate 4.3.8.Final 并且在部署到 WildFly 8.2.0.Final 期间发生错误。

21:19:24,283 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 88) MSC000001: Failed to start service jboss.persistenceunit."Example-1.0-SNAPSHOT.war#ExamplePU": org.jboss.msc.service.StartException in service jboss.persistenceunit."Example-1.0-SNAPSHOT.war#ExamplePU": javax.persistence.PersistenceException: [PersistenceUnit: ExamplePU] Unable to build Hibernate SessionFactory
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:172) [wildfly-jpa-8.2.0.Final.jar:8.2.0.Final]
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:117) [wildfly-jpa-8.2.0.Final.jar:8.2.0.Final]
at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.8.0_25]
at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:474) [wildfly-security-manager-1.0.0.Final.jar:1.0.0.Final]
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:182) [wildfly-jpa-8.2.0.Final.jar:8.2.0.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_25]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_25]
at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_25]
at org.jboss.threads.JBossThread.run(JBossThread.java:122) [jboss-threads-2.1.1.Final.jar:2.1.1.Final]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: ExamplePU] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1239) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.access$600(EntityManagerFactoryBuilderImpl.java:120) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:855) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:845) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:398) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:844) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.jboss.as.jpa.hibernate4.TwoPhaseBootstrapImpl.build(TwoPhaseBootstrapImpl.java:44) [jipijapa-hibernate4-3-1.0.1.Final.jar:]
at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:154) [wildfly-jpa-8.2.0.Final.jar:8.2.0.Final]
... 8 more
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property:  
de.company.project.somepackages.PictureRef.person in de.company.project.somepackages.Person.picturesOfCars
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:768) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:728) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:70) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1697) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
... 13 more

21:19:24,291 ERROR [org.jboss.as.controller.management-operation] (management-handler-thread - 2) JBAS014613: Operation ("deploy") failed - address: ([("deployment" => "Example-1.0-SNAPSHOT.war")]) - failure description: {"JBAS014671: Failed services" => {"jboss.persistenceunit.\"Example-1.0-SNAPSHOT.war#ExamplePU\"" => "org.jboss.msc.service.StartException in service jboss.persistenceunit.\"Example-1.0-SNAPSHOT.war#ExamplePU\": javax.persistence.PersistenceException: [PersistenceUnit: ExamplePU] Unable to build Hibernate SessionFactory
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: ExamplePU] Unable to build Hibernate SessionFactory
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: de.company.project.somepackages.PictureRef.person in de.company.project.somepackages.Person.picturesOfCars"}}
21:19:24,292 ERROR [org.jboss.as.server] (management-handler-thread - 2) JBAS015870: Deploy of deployment "Example-1.0-SNAPSHOT.war" was rolled back with the following failure message: 
{"JBAS014671: Failed services" => {"jboss.persistenceunit.\"Example-1.0-SNAPSHOT.war#ExamplePU\"" => "org.jboss.msc.service.StartException in service jboss.persistenceunit.\"Example-1.0-SNAPSHOT.war#ExamplePU\": javax.persistence.PersistenceException: [PersistenceUnit: ExamplePU] Unable to build Hibernate SessionFactory
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: ExamplePU] Unable to build Hibernate SessionFactory
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: de.company.project.somepackages.PictureRef.person in de.company.project.somepackages.Person.picturesOfCars"}}
Run Code Online (Sandbox Code Playgroud)

Nic*_*kJI 3

您\xe2\x80\x99 触及了 JPA 规范中未详细介绍的区域。JPA 规范 2.11 指出:

\n\n
\n

一个实体可以继承另一个实体类。实体支持继承、多态关联和多态查询。

\n
\n\n

这种支持到底是什么还没有定义。

\n\n

当您尝试将 a 映射@OneToMany到继承@ManyToOne关系\xe2\x80\xa6..

\n\n
public class Person extends AbstractEntity {\n\n    @OneToMany(mappedBy = "person", targetEntity = CarPictureRef.class)\n    private List<CarPictureRef> picturesOfCars;\n\n    @OneToMany(mappedBy = "person", targetEntity = HolidayPictureRef.class)\n    private List< HolidayPictureRef> picturesOfHolidays;\n\n    ...\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

休眠抱怨

\n\n
mappedBy reference an unknown target entity property\n
Run Code Online (Sandbox Code Playgroud)\n\n

person因此,当将mappedBy解析为子类时,Hibernate不允许继承字段(在本例中)。然而,关系定义中有足够的信息来解析这些关系,EclipseLink 做得很好。

\n\n

可以将超@ManyToOne类转移(PictureRef)到具体子类(CarPictureRef等等)

\n\n
@Entity\npublic class CarPictureRef extends PictureRef {\n\n    @ManyToOne\n    @JoinColumn(name = "person_id")\n    private Person person;\n\n    ...\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用此解决方案,您可以将@OneToMany映射保持Person原样。不幸的是,这在 Hibernate 中不起作用,因为 Hibernate 将查找pictureRef具有正确 person_ID 的任何子实体(在本例中为任何子类),然后抱怨找到的类型是错误的类型

\n\n
org.hibernate.WrongClassException: Object [id=55] was not of the specified subclass.\n
Run Code Online (Sandbox Code Playgroud)\n\n

同样,Hibernate 可以通过使用 Discriminator 列来解决这个问题,但它没有,而且 EclipseLink 确实这样了。

\n\n

解决这个问题的方法是为每个子类指定不同的连接列,这意味着随着每个新子类的出现,您\xe2\x80\x99将需要修改您的表,我认为这并不理想。

\n\n

使用 Hibernate,您可以将超PictureRef类定位在 @OneToMany 关系上,这样 Person 实体就变成了;

\n\n
@Entity\n@Table(name = "t_person")\n// Sequence Definition removed\npublic class Person extends AbstractEntity {\n\n    @OneToMany(mappedBy = "person", targetEntity = PictureRef.class)\n    private List<PictureRef> pictures;\n\n    ...\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,您将获得一个 PictureRef 列表,该列表将是任何子类。您可以测试列表中的项目并设置保存每个所需子类型的瞬态字段。所以从这个意义上说,Hibernate 支持多态关联,但是 eclipseLink 做得更多,所以要注意可移植性问题。

\n