Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA OneToMany list does not find mappedBy property which should be inherited

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 ofand 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 mappedByattribute 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"}}
like image 658
Christian Avatar asked Oct 30 '22 22:10

Christian


1 Answers

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.

like image 181
NickJI Avatar answered Nov 15 '22 05:11

NickJI