We currently work on some requirements where we have to add some similar Entities (Pictures of cars, of pets, holiday pictures, …) which all belong to one owner (Person) to the database. We don't directly link the byte array, but work with references. Later on maybe a lot more of picture types are added so to keep complexity low we want to directly link it to Java Classes so we can work with instance of
and similar stuff. We want to use @Inheritance
for a superclass PictureRef
which contains common attributes and also links to the person. Then there is another entity Person
which will have lists of these subclasses. This is a OneToMany
relation with a mappedBy
attribute. This mappedBy
attribute is unknown, so JPA returns us this error:
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
I think it's best illustrated with the code below. For readability I removed other attributes and getters/setters and id sequences.
1) All JPA Entites are derived from a abstract entity which holds id and auditing values. This works with a other subclasses correctly, so I don't think this class causes an issue.
Class AbstractEntity
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
@Id
// Sequence definition removed
private Long id;
// other values are following //
}
2) Then we have a superclass which has to contain a common attributes. All derived classes have to write in one table (because they look really similar). Also we will have business logic which works directly with this entity, e.g. load by id or delete.
Class 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;
}
3) Then there are at least two subclasses. More to come soon. They will contain attributes which are only relevant for this kind of Picture.
Class CarPictureRef
@Entity
@DiscriminatorValue("car")
public class CarPictureRef extends PictureRef {
@Column(name="licence_plate_visible")
private boolean licensePlateVisible;
}
Class HolidayPictureRef
@Entity
@DiscriminatorValue("holiday")
public class HolidayPictureRef extends PictureRef {
@Column(name="weather_condition")
private String weatherCondition;
}
4) And then there is the Person which owns/ uploaded all these pictures. The person has a list for each kind of pictures, as they are treated really different by the application. Each list contains concrete subclasses, but for the mappedBy
attribute we use the person
from the superclass PictureRef
. Maybe this kind of inheritance is not possible?
Class Person
@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 //
}
A workaround may be to only store everything with all attributes in one table (which we want to do anyway) and then also in only one Entity PictureRef
. Then we will write application logic in the backend which evaluates the pictureType
and creates new classes for the respective business case. But this seems somehow ugly - I would expect that there is a JPA solution for this common usecase?
Maybe we are just missing one ore more annotation?
For sake of completeness, I added the full stack trace. We are using Hibernate 4.3.8.Final and the error occurs during deployment to 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"}}
You’ve hit on an area that is not covered in the JPA spec in any great detail. The JPA Spec at 2.11 states:
An entity may inherit from another entity class. Entities support inheritance, polymorphic associations, and polymorphic queries.
Exactly what that support is is not defined.
When you attempt to map a @OneToMany
to a subclass which inherits the @ManyToOne
Relationship…..
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;
...
}
Hibernate complains
mappedBy reference an unknown target entity property
So Hibernate does not allow for the inheritance of fields (person
in this case) when resolving mappedBy to sublasses. There is however enough information in the relationship definitions to resolve these relationships and EclipseLink does this quite nicely.
It is possible to shift the @ManyToOne
from the super (PictureRef)
to the concrete subclasses (CarPictureRef
etc.)
@Entity
public class CarPictureRef extends PictureRef {
@ManyToOne
@JoinColumn(name = "person_id")
private Person person;
...
}
With this solution you leave the @OneToMany
mappings in Person
as they are. Unfortunately this does not work in Hibernate, as Hibernate will look for any child entity (any pictureRef
subclass in this case) that has the correct person_ID and then complain that the type found is the wrong type
org.hibernate.WrongClassException: Object [id=55] was not of the specified subclass.
Again, Hibernate could resolve this by using the Discriminator column but it does not, and again, this is something that EclipseLink does do.
Your way around this is to specify a different join column for each subclass, which means that as each new subclass comes along, you’ll need to modify your table which I guess is not ideal.
Using Hibernate, You can target the PictureRef
superclass on your @OneToMany relationship, such that the Person entity becomes;
@Entity
@Table(name = "t_person")
// Sequence Definition removed
public class Person extends AbstractEntity {
@OneToMany(mappedBy = "person", targetEntity = PictureRef.class)
private List<PictureRef> pictures;
...
}
You will then have a list of PictureRefs that will be any of the subclasses. You can test the items in the list and setup transient fields holding each of the desired subtypes. So to this extent, Hibernate supports polymorphic associations, but eclipseLink does a lot more, so beware portability issues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With