I defined 2 persistence units in my test persistence.xml to create 2 different H2 in-memory database for each integration test connecting db.

The tests are fine and passing when run separately, but when I run all tests, only the first passes, and the second fails, with:

java.lang.IllegalArgumentException: Not an entity: class com.data.item.ItemHistory
    at org.hibernate.ejb.metamodel.MetamodelImpl.entity(MetamodelImpl.java:184)
    at org.hibernate.ejb.criteria.QueryStructure.from(QueryStructure.java:138)
    at org.hibernate.ejb.criteria.CriteriaQueryImpl.from(CriteriaQueryImpl.java:179)

I have @Entity annotation on the class, apparently.

And, even I tell H2 to destroy when last connection gone(DB_CLOSE_ON_EXIT=TRUE;DB_CLOSE_DELAY=0;), and use different name of db in each PU, this still happens.

My persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- For H2 database integration tests. -->
<!-- For each int test, define unique name PU in this file and include SQL files in different paths. -->
<persistence version="2.0"
             xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="ItemHistoryPersistenceServiceBeanIntTest" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- this is the correct provider, differ from src/main -->
        <class>com.data.company.Company</class>
        <class>com.data.item.ItemHistory</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:h2:mem:test1;DB_CLOSE_ON_EXIT=TRUE;DB_CLOSE_DELAY=0;MODE=Oracle;INIT=
                      RUNSCRIPT FROM 'src/test/resources/db/ddl/init.sql'\;
                      RUNSCRIPT FROM 'src/test/resources/db/ddl/company.sql'\;
                      RUNSCRIPT FROM 'src/test/resources/db/ddl/item-history.sql'\;
                      RUNSCRIPT FROM 'src/test/resources/db/dml/company-data.sql'\;
                      RUNSCRIPT FROM 'src/test/resources/db/dml/item-history-data.sql'\;"
            />
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.id.new_generator_mappings" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/> <!-- only "update" allows DML -->
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.default_schema" value="APP"/>
        </properties>
    </persistence-unit>

    <persistence-unit name="CompanyPersistenceServiceBeanIntTest" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>com.data.company.Company</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:h2:mem:test2;DB_CLOSE_ON_EXIT=TRUE;DB_CLOSE_DELAY=0;MODE=Oracle;INIT=
                      RUNSCRIPT FROM 'src/test/resources/db/ddl/init.sql'\;
                      RUNSCRIPT FROM 'src/test/resources/db/ddl/company.sql'\;
                      RUNSCRIPT FROM 'src/test/resources/db/dml/company-data.sql'\;"
            />
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.id.new_generator_mappings" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.default_schema" value="APP"/>
        </properties>
    </persistence-unit>
</persistence>

This prevents me from using H2; why I cannot use multiple in-memory database at the same time?

I think it has something to do with the ddl-auto property? But when I put other values, data are not inserted into database; only update works.

Can I have multiple in-memory H2 database like this? If not, what other modes work for multiple database at the same time? Tried tcp but no avail. I tried to define database in embedded mode(in physical files), but not working. I changed schema in SQL and in xml to distinct name in each PU, not working.

1

There are 1 best solutions below

0
On

I found out why... I have a base integration test which reuses the EntityManagerFactory (stop creating new factory when there is one), so the reuse caused the problem.

public abstract class H2DBIntBaseTest<T> {

    protected EntityManager realEntityManager;
    protected static EntityManagerFactory factory;
    protected T serviceBean;

    public abstract T initServiceBean();


    @Before
    public void setupEntityManager() {
        // we don't care double creation/sychronization here, as they are the same
            // Make sure the PU name in persistence.xml matches the test class name
        if (factory == null) { // <----- here is the problem!
            factory = Persistence.createEntityManagerFactory(getClass().getSimpleName());
        }

        realEntityManager = factory.createEntityManager();

        EntityManager spy = spy(realEntityManager);

        serviceBean = initServiceBean();

        try {
            // inject the real entity manager, instead of using mocks
            Field entityManagerField = serviceBean.getClass().getDeclaredField("entityManager");
            entityManagerField.setAccessible(true);
            entityManagerField.set(serviceBean, spy);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new AssertionError("should not reach here");
        }
    }

    @After
    public void teardown() {
        realEntityManager.close();
    }
}

I removed the line of checking null, and it works.

It's not H2, not Hibernate, is my code error.