Search code examples
javahibernatejpahibernate-mapping

DuplicateMappingException after extending @Entity with @Inheritance


I have a table my_entity that is referenced by multiple @Entity annotated classes like so:

@Immutable
@Entity
@Table(name = "my_entity")
public class EntityA { ... }
@Entity
@Table(name = "my_entity")
public class EntityB { ... }

This works perfectly fine.

I now have to change one of these entities to extend an @Inheritance annotated class to allow for polymorphic queries:

@Immutable
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Parent { ... }
@Immutable
@Entity
@Table(name = "my_entity")
public class EntityA extends Parent { ... }
@Entity
@Table(name = "my_entity")
public class EntityB { ... }

I then get a org.hibernate.DuplicateMappingException: Duplicate table my_entity

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.orm.jpa.SharedEntityManagerCreator#0': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: Duplicate table mapping my_entity
    at app//org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
    at app//org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
    at app//org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:688)
    at app//org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:505)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at app//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at app//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at app//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at app//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at app//org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
    at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
    at app//org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911)
    at app//org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788)
    ... 193 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: Duplicate table mapping my_entity
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at app//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at app//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at app//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at app//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at app//org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
    ... 209 more
Caused by: org.hibernate.DuplicateMappingException: Duplicate table mapping my_entity
    at app//org.hibernate.boot.internal.InFlightMetadataCollectorImpl.addDenormalizedTable(InFlightMetadataCollectorImpl.java:799)
    at app//org.hibernate.cfg.annotations.TableBinder.buildAndFillTable(TableBinder.java:488)
    at app//org.hibernate.cfg.annotations.EntityBinder.bindTable(EntityBinder.java:849)
    at app//org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:642)
    at app//org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:225)
    at app//org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:239)
    at app//org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:282)
    at app//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1460)
    at app//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1494)
    at app//org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
    at app//org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
    at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
    at app//org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
    at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
    ... 216 more

Is there a way to still reference the same table in an inheritance hierarchy?

My workaround would be to create a view based on the my_entity table and use this for the mapping of EntityA, but I would prefer a solution based on the entity definition if there exists one.

I also don't understand why using @Inheritance would create this issue while it was working fine before.

I'm using Hibernate 5.6.15.


Solution

  • in InFlightMetadataCollectorImpl.addDenormalizedTable we have the following

    ...
    if ( subselectFragment != null ) {
            return new DenormalizedTable( namespace, logicalName, subselectFragment, isAbstract, includedTable );
        }
        else {
            Table table = namespace.locateTable( logicalName );
            if ( table != null ) {
                throw new DuplicateMappingException( DuplicateMappingException.Type.TABLE, logicalName.toString() );
            }
            else {
                table = namespace.createDenormalizedTable( logicalName, isAbstract, includedTable );
            }
            return table;
        }
    
    • the exception is raised at line 799 that is throw new DuplicateMappingException
    • like the https://hibernate.atlassian.net/browse/HHH-8277 says, the problem is there only when the simple entity is loaded before the complex entity; the problem is gone when the complex entity is loaded after the simple entity.
    • the order in which the entities are loaded seems to be the same order the associated java classes are added in the jar

    possible general solutions

    • check if this is ongoing in hibernate 6, probably yes
    • create a pull request with a fix in hibernate 5 (could be complex unless instructed by hibernate gurus on what the solution could be)

    possible personal solutions

    • if there is a way to determine the order in which java classes are added in a jar you could influence it and make the complex entity class be added in the jar before the simple entity class
    • otherwise at runtime update the order of the entries in the PersistenceUnitInfo.getManagedClassNames collection so that the className associated to the complex entity comes before the className associated to the simple entity