Reputation: 3683
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
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
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