Frederick Zhang
Frederick Zhang

Reputation: 3683

'mappedBy reference an unknown target entity property' when properties with the same name exist in both parent and child entities

I'm on Spring Boot v1.5.3 and when I use Hibernate inheritance, AnnotationException: mappedBy reference an unknown target entity property occurs when there are properties with the same name in both parent and child entities.

For example:

Apple => RedApple
      => GreenApple
      => etc.
Tree  => RedAppleTree
      => GreenAppleTree
      => etc.
// Tree.java

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode(exclude = {"apples"})
@DiscriminatorColumn(name = "colour")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Tree {
    @Id
    @GeneratedValue
    private Long id;
}
// RedAppleTree.java

@Entity
@Table(name = "tree")
@DiscriminatorValue("RED")
public class RedAppleTree extends Tree {
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "tree")
    private List<RedApple> apples;
}
// Apple.java

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@DiscriminatorColumn(name = "colour")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Apple {
    @Id
    @GeneratedValue
    private Long id;

    @Enumerated(EnumType.STRING)
    @Column(insertable = false, updatable = false)
    private Colour colour;

    @JoinColumn(insertable = false, updatable = false)
    @ManyToOne
    private Tree tree;
}
// RedApple.java

@Entity
@Table(name = "apple")
@DiscriminatorValue("RED")
public class RedApple extends Apple {
    @JoinColumn(insertable = false, updatable = false)
    @ManyToOne
    private RedAppleTree tree;
}
// Colour.java

public enum Colour {
    RED,
    GREEN,
}

Apple.tree and RedApple.tree exist at the same time. In parent Apple the type is Tree; in child RedApple it's RedAppleTree.

Now when the project runs, I get:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.example.demo.dao.RedApple.tree in com.example.demo.dao.RedAppleTree.apples
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean (AbstractAutowireCapableBeanFactory.java:1628)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject (AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:197)
    at org.springframework.context.support.AbstractApplicationContext.getBean (AbstractApplicationContext.java:1081)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:856)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:542)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh (EmbeddedWebApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:737)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:370)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:314)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1162)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1151)
    at com.example.demo.DemoApplication.main (DemoApplication.java:13)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:527)
    at java.lang.Thread.run (Thread.java:834)
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.example.demo.dao.RedApple.tree in com.example.demo.dao.RedAppleTree.apples
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass (CollectionBinder.java:769)
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass (CollectionBinder.java:719)
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass (CollectionSecondPass.java:54)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses (InFlightMetadataCollectorImpl.java:1655)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses (InFlightMetadataCollectorImpl.java:1623)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete (MetadataBuildingProcess.java:278)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata (EntityManagerFactoryBuilderImpl.java:847)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build (EntityManagerFactoryBuilderImpl.java:874)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory (SpringHibernateJpaPersistenceProvider.java:60)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory (LocalContainerEntityManagerFactoryBean.java:353)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory (AbstractEntityManagerFactoryBean.java:370)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet (AbstractEntityManagerFactoryBean.java:359)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods (AbstractAutowireCapableBeanFactory.java:1687)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean (AbstractAutowireCapableBeanFactory.java:1624)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean (AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean (AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject (AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton (DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean (AbstractBeanFactory.java:197)
    at org.springframework.context.support.AbstractApplicationContext.getBean (AbstractApplicationContext.java:1081)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization (AbstractApplicationContext.java:856)
    at org.springframework.context.support.AbstractApplicationContext.refresh (AbstractApplicationContext.java:542)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh (EmbeddedWebApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh (SpringApplication.java:737)
    at org.springframework.boot.SpringApplication.refreshContext (SpringApplication.java:370)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:314)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1162)
    at org.springframework.boot.SpringApplication.run (SpringApplication.java:1151)
    at com.example.demo.DemoApplication.main (DemoApplication.java:13)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:527)
    at java.lang.Thread.run (Thread.java:834)

Now I can rename one of the properties to work around this problem but in the actual project I'm working on it'd lead to more changes.

Is it possible to keep the property name untouched?

Upvotes: 0

Views: 3353

Answers (2)

Nathan
Nathan

Reputation: 1659

It is a problem to duplicate your "tree" property in both your superclass and your subclass. I believe that this example here will do what you want.

Tree.java

@Entity
@Table(name = "tree")
@DiscriminatorColumn(name = "colour")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Tree<APPLE extends Apple> {

    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "tree", targetEntity = Apple.class)
    private Set<APPLE> apples = Sets.newHashSet();

    //... getters & setters
}

Apple.java

@Table(name = "apple")
@Entity
@DiscriminatorColumn(name = "colour")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Apple<TREE extends Tree> {

    @Id
    @GeneratedValue
    private Long id;

    @JoinColumn(name = "tree_id")
    @ManyToOne(targetEntity = Tree.class)
    private TREE tree;

}

RedAppleTree.java

@Entity
@Table(name = "tree")
@DiscriminatorColumn(name = "colour")
@DiscriminatorValue("RED")
public class RedAppleTree extends Tree<RedApple> {
}

RedApple.java

@Entity
@Table(name = "apple")
@DiscriminatorColumn(name = "colour")
@DiscriminatorValue("RED")
public class RedApple extends Apple<RedAppleTree> {
}

This creates the following DB tables

create table tree (
   colour varchar(31) not null,
    id bigint generated by default as identity (start with 1),
    primary key (id)
);
create table apple (
   colour varchar(31) not null,
    id bigint generated by default as identity (start with 1),
    tree_id bigint,
    primary key (id)
)

Then, when I run the following code

@Test
public void test() {

    RedAppleTree tree = new RedAppleTree();
    session.persist(tree);

    GreenAppleTree greenTree = new GreenAppleTree();
    session.persist(greenTree);

    RedApple apple = new RedApple();
    apple.setTree(tree);
    session.persist(apple);

    tree.getApples().add(apple);
    session.flush();
    session.clear();
    
    tree = session.get(RedAppleTree.class, tree.getId());

    System.err.println(session.createCriteria(RedAppleTree.class).list());
}

This results in the following SQL (ran with hibernate-sql-logging enabled)

Hibernate: 
insert 
into
    tree
    (id, version, colour) 
values
    (default, ?, 'RED')
Hibernate: 
insert 
into
    tree
    (id, version, colour) 
values
    (default, ?, 'GREEN')
Hibernate: 
insert 
into
    apple
    (id, version, tree_id, colour) 
values
    (default, ?, ?, 'RED')
Hibernate: 
select
    redappletr0_.id as id2_409_0_,
    redappletr0_.version as version3_409_0_ 
from
    tree redappletr0_ 
where
    redappletr0_.id=? 
    and redappletr0_.colour='RED'
Hibernate: 
select
    this_.id as id2_409_0_,
    this_.version as version3_409_0_ 
from
    tree this_ 
where
    this_.colour='RED'

I believe that this ought to solve your basic problem.

Upvotes: 1

JLazar0
JLazar0

Reputation: 1292

The truth is that I have never encountered a similar problem, the logical thing, and a good practice would be to change the name of the property to differentiate which entity each one refers to, the compiler will tell you where to change it.

You could try indicating the child class in the relation as follows:

mappedBy = "tree", targetEntity = RedAppleTree.class

But I reiterate, the best option would be to change the name of the property, even if it involves more work.

Upvotes: 0

Related Questions