Search code examples
javaspringhibernateentitymanager

Spring/Hibernate fails to initialize the EntityManager when using mix of annotations and hbm for entities


I have a Spring/Hibernate project with entities defined using both hbm's and annotations.

When I am trying to deploy the project I get the following error message:

Caused by: org.hibernate.MappingException: Following super classes referenced in extends not found: hibernate.examples.model.Task
    at org.hibernate.cfg.Configuration.processExtendsQueue(Configuration.java:1768) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1690) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
    ....................

The reason I have to keep using both is because some of the code is legacy and I cannot touch that at the moment. And we have to start using the annotations for creating any new entities moving forward.

This is how I am registering both kinds of mappings into the configuration:

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan("hibernate.examples.model", "models");
        em.setMappingResources("Task.hbm.xml");
        em.setMappingResources("HealthTask.hbm.xml");

        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibernateProperties());
        return em;
    }

The setPackagesToScan method scans for all the entities marked by annotations and setMappingResources is supposed to take care of the hbm files.

When I comment out the em.setMappingResources..... then the project gets deployed without any issues.

Also, the weird part to note is that when I run it as a standalone application i.e. using public static void main(String[] args) I do not have any issues working with both kinds of mapping. All the entities get registered and schema gets created.

Here is how I run the project:

public class ConceptRunner {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

        ctx.register(HibernateConfiguration.class);
        ctx.refresh();

        System.exit(0);

    }

}

But as soon as I deploy the project, it fails to register the hbm files.

Here are my hbm files:

Task.hbm.xml

<hibernate-mapping>
    <class name="hibernate.examples.model.Task" table="TASKS" abstract="true">
        <id name="id" type="java.lang.Long">
            <column name="ID" precision="22" scale="0"/>
            <generator class="identity"/>
        </id>
        <discriminator column="TYPE" type="string"/>
        <property name="type" type="java.lang.String" insert="false" update="false">
            <column name="TYPE" length="10"/>
        </property>
        <property name="name" type="java.lang.String">
            <column name="NAME" length="50"/>
        </property>
        <property name="description" type="java.lang.String">
            <column name="DESCRIPTION" length="250"/>
        </property>
    </class>
</hibernate-mapping>

HealthTask.hbm.xml

<hibernate-mapping>
    <subclass name="hibernate.examples.model.HealthTask"
              extends="hibernate.examples.model.Task" discriminator-value="HEALTH">
        <property name="requestServed" type="java.lang.Long">
            <column name="REQUEST_SERVED"/>
        </property>
        <property name="requestFailed" type="java.lang.Long">
            <column name="REQUEST_FAILED"/>
        </property>
        <property name="totalRequest" type="java.lang.Long">
            <column name="TOTAL_REQUEST"/>
        </property>
    </subclass>
</hibernate-mapping>

Please let me know if there is any other information that you would like from me..


Solution

  • @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan("hibernate.examples.model", "models");
        em.setMappingResources("Task.hbm.xml");
        em.setMappingResources("HealthTask.hbm.xml");
    
        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibernateProperties());
        return em;
    }
    

    As the setMappingResources is a setter each call to it will replace the current set of resources. As you are calling it twice in your code only the last one will remain (the HealthTask.hbm.xml and the Task.hbm.xml will be ignored).

    If you take a look at the setMappingResources of the LocalContainerEntityManagerFactoryBean you will notice it takes a String... as an argument (a varargs argument to be exact).

    So instead of calling it twice call it once with 2 arguments.

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan("hibernate.examples.model", "models");
        em.setMappingResources("Task.hbm.xml", "HealthTask.hbm.xml");
    
        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibernateProperties());
        return em;
    }