How to configure JPA for testing in Maven

JavaTestingMaven 2JpaIntegration Testing

Java Problem Overview


Is there a way to set up a second persistence.xml file in a Maven project such that it is used for testing instead of the normal one that is used for deployment?

I tried putting a persistence.xml into src/test/resources/META-INF, which gets copied into target/test-classes/META-INF, but it seems target/classes/META-INF (the copy from the src/main/resources) gets preferred, despite mvn -X test listing the classpath entries in the right order:

[DEBUG] Test Classpath :
[DEBUG]   /home/uqpbecke/dev/NetBeansProjects/UserManager/target/test-classes
[DEBUG]   /home/uqpbecke/dev/NetBeansProjects/UserManager/target/classes
[DEBUG]   /home/uqpbecke/.m2/repository/junit/junit/4.5/junit-4.5.jar
...

I would like to be able to run tests against a simple hsqldb configuration without having to change the deployment version of the JPA configuration, ideally straight after project checkout without any need for local tweaking.

Java Solutions


Solution 1 - Java

The following will work for Maven 2.1+ (prior to that there wasn't a phase between test and package that you could bind an execution to).

You can use the maven-antrun-plugin to replace the persistence.xml with the test version for the duration of the tests, then restore the proper version before the project is packaged.

This example assumes the production version is src/main/resources/META-INF/persistence.xml and the test version is src/test/resources/META-INF/persistence.xml, so they will be copied to target/classes/META-INF and target/test-classes/META-INF respectively.

It would be more elegant to encapsulate this into a mojo, but as you're only copying a file, it seems like overkill.

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.3</version>
  <executions>
    <execution>
      <id>copy-test-persistence</id>
      <phase>process-test-resources</phase>
      <configuration>
        <tasks>
          <!--backup the "proper" persistence.xml-->
          <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
          <!--replace the "proper" persistence.xml with the "test" version-->
          <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
    <execution>
      <id>restore-persistence</id>
      <phase>prepare-package</phase>
      <configuration>
        <tasks>
          <!--restore the "proper" persistence.xml-->
          <copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Solution 2 - Java

In an EE6/CDI/JPA project, a test src/test/resources/META-INF/persistence.xml is picked up just fine without any further configuration.

When using JPA in Spring, the following works in the application context used for testing:

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <!--
        JPA requires META-INF/persistence.xml, but somehow prefers the one
        in classes/META-INF over the one in test-classes/META-INF. Spring
        to the rescue, as it allows for setting things differently, like by
        referring to "classpath:persistence-TEST.xml". Or, simply referring
        to "META-INF/persistence.xml" makes JPA use the test version too: 
    -->
    <property name="persistenceXmlLocation" value="META-INF/persistence.xml" />

    <!-- As defined in /src/test/resources/META-INF/persistence.xml -->
    <property name="persistenceUnitName" value="myTestPersistenceUnit" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        </bean>
    </property>
</bean>

Here, /src/test/resources/META-INF/persistence.xml (copied into target/test-classes) would be preferred over /src/main/resources/META-INF/persistence.xml (copied into target/classes).

Unfortunately, the location of the persistence.xml file also determines the so-called "persistence unit's root", which then determines which classes are scanned for @Entity annotations. So, using /src/test/resources/META-INF/persistence.xml would scan classes in target/test-classes, not classes in target/classes (where the classes that need to be tested would live).

Hence, for testing, one would need to explicitly add <class> entries to persistence.xml, to avoid java.lang.IllegalArgumentException: Not an entity: class .... The need for <class> entries can be avoided by using a different file name, like persistence-TEST.xml, and put that file in the very same folder as the regular persistence.xml file. The Spring context from your test folder can then just refer to <property name="persistenceXmlLocation" value="META-INF/persistence-TEST.xml" />, and Spring will find it for you in src/main.

As an alternative, one might be able to keep persistence.xml the same for the actual application and the tests, and only define one in src/main. Most configuration such as the drivers, dialect and optional credentials can be done in the Spring context instead. Also settings such as hibernate.hbm2ddl.auto can be passed in the context:

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!-- For example: com.mysql.jdbc.Driver or org.h2.Driver -->
    <property name="driverClassName" value="#{myConfig['db.driver']}" />
    <!-- For example: jdbc:mysql://localhost:3306/myDbName or 
        jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 -->
    <property name="url" value="#{myConfig['db.url']}" />
    <!-- Ignored for H2 -->
    <property name="username" value="#{myConfig['db.username']}" />
    <property name="password" value="#{myConfig['db.password']}" />
</bean>

<bean id="jpaAdaptor"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <!-- For example: org.hibernate.dialect.MySQL5Dialect or 
        org.hibernate.dialect.H2Dialect -->
    <property name="databasePlatform" value="#{myConfig['db.dialect']}" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaAdapter" />
    <property name="jpaProperties">
        <props>
            <!-- For example: validate, update, create or create-drop -->
            <prop key="hibernate.hbm2ddl.auto">#{myConfig['db.ddl']}</prop>
            <prop key="hibernate.show_sql">#{myConfig['db.showSql']}</prop>
            <prop key="hibernate.format_sql">true</prop>
        </props>
    </property>
</bean>

Solution 3 - Java

It seems multiple persistence.xml files is a general problem with JPA, solved only by classloading tricks.

A workaround that works for me is to define multiple persistence units in one persistence.xml file and then make sure that your deployment and test code use a different binding (in Spring you can set the "persistenceUnitName" property on the entity manager factory). It pollutes your deployment file with the test configuration, but if you don't mind that it works ok.

Solution 4 - Java

Add a persistance.xml for tests: /src/test/resources/META-INF/persistence.xml As @Arjan said, that would change persistance unit's root and entity classes would be scanned in target/test-classes. To handle that, add jar-file element to this persistance.xml:

/src/test/resources/META-INF/persistence.xml

<persistence 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"
             version="2.0">
    <persistence-unit name="com.some.project">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
		<jar-file>${project.basedir}/target/classes</jar-file>
		<properties>
			<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test_database" />
			<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.user" value="user" />
            <property name="javax.persistence.jdbc.password" value="..." />
        </properties>
    </persistence-unit>
</persistence>

Then, add filtering of test resources to your pom.xml:

<project>
	...
	<build>
		...
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
                <filtering>true</filtering>
            </testResource>
        </testResources>
        ...
    </build>
...
</project>

This will work because jar-file can target to directories, not only to jar files.

Solution 5 - Java

I prefer the solution of using different persistence.xml for testing and production as Rich Seller [post][1] (thanks!!).

But need to change:

<copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>

for:

<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>

In order persistence.xml.proper not embedded in .jar file [1]: https://stackoverflow.com/questions/385532/how-to-configure-jpa-for-testing-in-maven/1179876#1179876

Solution 6 - Java

I tried the ClassLoaderProxy approach but had the problem that the JPA annotated classes are not handled as persistent classes by hibernate.

So decided to try it without using persistence.xml. The advantage is that the maven build and the Eclipse JUnit test will work without modifications.

I have a persitent support class for JUnit testing.

public class PersistenceTestSupport {

	protected EntityManager em;
	protected EntityTransaction et;

	/**
	 * Setup the the {@code EntityManager} and {@code EntityTransaction} for
	 * local junit testing.
	 */
	public void setup() {
		
		Properties props = new Properties();
		props.put("hibernate.hbm2ddl.auto", "create-drop");
		props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
		props.put("hibernate.connection.url", "jdbc:mysql://localhost/db_name");
		props.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
		props.put("hibernate.connection.username", "user");
		props.put("hibernate.connection.password", "****");

		Ejb3Configuration cfg = new Ejb3Configuration();
		em = cfg.addProperties(props)
			.addAnnotatedClass(Class1.class)
			.addAnnotatedClass(Class2.class)
			...
                    .addAnnotatedClass(Classn.class)
			.buildEntityManagerFactory()
			.createEntityManager();

		et = em.getTransaction();
	}
}

My test classes just extend PersistenceTestSupport and call the setup() in TestCase.setup().

The only drawback is to keep the persistent classes up todate, but for JUnit testing this is acceptable for me.

Solution 7 - Java

This answer might sounds silly but I was looking for a way which lets me run those tests from eclipse by Run As -> JUnit Test. This is how I made it:

@BeforeClass
public static void setUp() throws IOException {
    Files.copy(new File("target/test-classes/META-INF/persistence.xml"), new File("target/classes/META-INF/persistence.xml"));
    // ...
}

I'm just copying the test/persistence.xml to classes/persistence.xml. This works.

Solution 8 - Java

Keep two copies of persistence.xml file. One for testing and another for normal build.

The default life cycle copy the build persistence.xml to src/test/resources/META-INF

Create a separate profile which when run will copy the testing persistence.xml to src/test/resources/META-INF

Solution 9 - Java

Persistence.xml is used as a starting point to search for entity classes unless you list all classes explicitly and add . So if you want to override this file with another one, say from src/test/resources, you have to specify every single entity class in this second persistence.xml otherwise no entity class would be found.

Another solution would be to overwrite the file using the maven-resources-plugin ('copy-resources' goal). But then you have to overwrite it twice, once for testing (e.g. phase process-test-classes) and once for the real packaging (phase 'prepare-package').

Solution 10 - Java

This is an extension of Rich Seller's answer with proper handling of Hibernate finding multiple persistence.xml files on the classpath and pre-testing state restoration.

Setup:

Create one persistence file for deployment/packaging and one for testing:

  • src/main/resources/persistence.xml

  • src/test/resources/persistence-testing.xml

in your pom.xml add this to the plugins section:

        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.3</version>
            <executions>
                <execution>
                    <id>copy-test-persistence</id>
                    <phase>process-test-resources</phase>
                    <configuration>
                        <tasks>
                            <echo>renaming deployment persistence.xml</echo>
                            <move file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
                            <echo>replacing deployment persistence.xml with test version</echo>
                            <copy file="${project.build.testOutputDirectory}/META-INF/persistence-testing.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
                <execution>
                    <id>restore-persistence</id>
                    <phase>prepare-package</phase>
                    <configuration>
                        <tasks>
                            <echo>restoring the deployment persistence.xml</echo>
                            <move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

Advantages over other solutions

  • No extra Java code required
  • Only one persistence.xml on classpath
  • Both building and testing work as expected
  • Describing output on console (echo)
  • For packaging the state is 100% restored. No leftover files

Solution 11 - Java

I'm trying to do the same thing. I have a solution that works for me - yours may vary (and you might not love the solution... it's a bit low-level).

I came across an article on the net where they were using a custom class loader to do something similar which served as inspiration. If anyone can see how to improve then suggestions would be welcome btw. For deployment I rely on container injection of the EntityManager but for testing I create it myself using this code:

final Thread currentThread = Thread.currentThread();
final ClassLoader saveClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new ClassLoaderProxy(saveClassLoader));

EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test");
em = emFactory.createEntityManager();

Then the ClassLoaderProxy is about as minimal as you can get and just redirects requests for META-INF/persistence.xml to META-INF/test-persist.xml:

public class ClassLoaderProxy extends ClassLoader {

	public ClassLoaderProxy(final ClassLoader parent) {
		super();
	}

	@Override
	public Enumeration<URL> getResources(final String name) throws IOException {
		if (!"META-INF/persistence.xml".equals(name)) {
			return super.getResources(name);
		} else {
			System.out.println("Redirecting persistence.xml to test-persist.xml");
			return super.getResources("META-INF/test-persist.xml");
		}
	}
}

Just to explain this a bit more:

  1. There are two persistence.xml files (one named persistence.xml that is used outside testing and one named test-persist.xml that is used for tests).
  2. The custom class loader is only active for unit tests (for deployment everything is normal)
  3. The custom class loader redirects requests for "META-INF/persistence.xml" to the test version ("META-INF/test-persist.xml").

I was originally hitting some problems because Hibernate will revert back (somehow) to the classloader that was used to load Hibernate (at least I think that is what was going on). I've found that putting the ClassLoader switching code (the first block) as a static block in your Test case it will get loaded before Hibernate but that, depending on your unit test structure you may also need to put the same code in other places (yuck).

Solution 12 - Java

Another approach is to use a separate persistence.xml for testing (test/../META-INF/persistence.xml but override the Scanner as follows: -

testing persistence.xml needs to contain

<property name="hibernate.ejb.resource_scanner" value = "...TestScanner" />

Code for new class TestScanner is as follows.

import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;
import org.hibernate.ejb.packaging.NamedInputStream;
import org.hibernate.ejb.packaging.NativeScanner;


public class TestScanner extends NativeScanner
{

@Override
public Set <Class <?> > 
getClassesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
{  return super.getClassesInJar (getUpdatedURL (jar), annotations); }

@Override
public Set <NamedInputStream> 
getFilesInJar (URL jar, Set <String> patterns)
{  return super.getFilesInJar (getUpdatedURL (jar), patterns); }

@Override
public Set <Package> 
getPackagesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
{  return super.getPackagesInJar (getUpdatedURL (jar), annotations); }

private URL getUpdatedURL (URL url)
{
  String oldURL = url.toExternalForm ();
  String newURL = oldURL.replaceAll ("test-classes", "classes");
  URL result;
  try {
    result = newURL.equals (oldURL) ? url : new URL (newURL);
  } catch (MalformedURLException e)
  {  // Whatever  }
  return result;
}

}
 

Solution 13 - Java

When using OpenEJB, persistence.xml can be overriden with alternate descriptors: http://tomee.apache.org/alternate-descriptors.html

Solution 14 - Java

Another option for this use case would be adding multiple persistence units, one for lets say production and another one for testing and inject the EntityManagerFactory accordingly.

Place both persistence-units into the persistence.xml of the actual project and have your test cases inject the correct EntityManager. The example below illustrates how to do that with guice. Please note that I've left in some mockito mocking for completeness, the mockito specific code has been marked accordingly and is not required for injection.

public class HibernateTestDatabaseProvider extends AbstractModule {
	private static final ThreadLocal<EntityManager> ENTITYMANAGER_CACHE = new ThreadLocal<>();

	@Override
	public void configure() {
	}

	@Provides
	@Singleton
	public EntityManagerFactory provideEntityManagerFactory() {
		return Persistence.createEntityManagerFactory("my.test.persistence.unit");
	}

	@Provides
	public CriteriaBuilder provideCriteriaBuilder(EntityManagerFactory entityManagerFactory) {
		return entityManagerFactory.getCriteriaBuilder();
	}
    
	@Provides
	public EntityManager provideEntityManager(EntityManagerFactory entityManagerFactory) {
		EntityManager entityManager = ENTITYMANAGER_CACHE.get();
		if (entityManager == null) {
			// prevent commits on the database, requires mockito. Not relevant for this answer
			entityManager = spy(entityManagerFactory.createEntityManager());


			EntityTransaction et = spy(entityManager.getTransaction());
			when(entityManager.getTransaction()).thenReturn(et);
			doNothing().when(et).commit();

			ENTITYMANAGER_CACHE.set(entityManager);
		}
		return entityManager;
	}
}

Solution 15 - Java

put tests in own maven project with its persistence.xml

Solution 16 - Java

I'd suggest using different maven profiles where you could filter your database.proprerties files and have one database.properties per profile.

This way you don't have to keep duplicates of any other configuration files except for the .properties.

<properties>
    <!-- Used to locate the profile specific configuration file. -->
    <build.profile.id>default</build.profile.id>
    <!-- Only unit tests are run by default. -->
    <skip.integration.tests>true</skip.integration.tests>
    <skip.unit.tests>false</skip.unit.tests>
    <integration.test.files>**/*IT.java</integration.test.files>
</properties>
<profiles>
    <profile>
        <id>default</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <!--
                Specifies the build profile id, which is used to find out the correct properties file.
                This is not actually necessary for this example, but it can be used for other purposes.
            -->
            <build.profile.id>default</build.profile.id>
            <skip.integration.tests>true</skip.integration.tests>
            <skip.unit.tests>false</skip.unit.tests>
        </properties>
        <build>
            <filters>
                <!--
                    Specifies path to the properties file, which contains profile specific
                    configuration. In this case, the configuration file should be the default spring/database.properties file
                -->
                <filter>src/main/resources/META-INF/spring/database.properties</filter>
            </filters>
            <resources>
                <!--
                    Placeholders found from files located in the configured resource directories are replaced
                    with values found from the profile specific configuration files.
                -->
                <resource>
                    <filtering>true</filtering>
                    <directory>src/main/resources</directory>
                    <!--
                        You can also include only specific files found from the configured directory or
                        exclude files. This can be done by uncommenting following sections and adding
                        the configuration under includes and excludes tags.
                    -->
                    <!--
                    <includes>
                        <include></include>
                    </includes>
                    <excludes>
                        <exclude></exclude>
                    </excludes>
                    -->
                </resource>
            </resources>
        </build>
    </profile>
    <profile>
        <id>integration</id>
        <properties>
            <!--
                Specifies the build profile id, which is used to find out the correct properties file.
                This is not actually necessary for this example, but it can be used for other purposes.
            -->
            <build.profile.id>integration</build.profile.id>
            <skip.integration.tests>false</skip.integration.tests>
            <skip.unit.tests>true</skip.unit.tests>
        </properties>
        <build>
            <filters>
                <!--
                    Specifies path to the properties file, which contains profile specific
                    configuration. In this case, the configuration file is searched
                    from spring/profiles/it/ directory.
                -->
                <filter>src/main/resources/META-INF/spring/profiles/${build.profile.id}/database.properties</filter>
            </filters>
            <resources>
                <!--
                    Placeholders found from files located in the configured resource directories are replaced
                    with values found from the profile specific configuration files.
                -->
                <resource>
                    <filtering>true</filtering>
                    <directory>src/main/resources</directory>
                    <!--
                        You can also include only specific files found from the configured directory or
                        exclude files. This can be done by uncommenting following sections and adding
                        the configuration under includes and excludes tags.
                    -->
                    <!--
                    <includes>
                        <include></include>
                    </includes>
                    <excludes>
                        <exclude></exclude>
                    </excludes>
                    -->
                </resource>
            </resources>
        </build>
    </profile>
</profiles>

With the help of surefire for unit tests and failsfe for integration tests, you're done.

    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.12</version>
    <configuration>
        <junitArtifactName>org.junit:com.springsource.org.junit</junitArtifactName>
        <!--see: https://issuetracker.springsource.com/browse/EBR-220-->
        <printSummary>false</printSummary>
        <redirectTestOutputToFile>true</redirectTestOutputToFile>
        <!-- Skips unit tests if the value of skip.unit.tests property is true -->
        <skipTests>${skip.unit.tests}</skipTests>
        <!-- Excludes integration tests when unit tests are run. -->
        <excludes>
            <exclude>${integration.test.files}</exclude>
        </excludes>
    </configuration>
</plugin>


<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.12</version>
    <configuration>
        <!-- Skips integration tests if the value of skip.integration.tests property is true -->
        <skipTests>${skip.integration.tests}</skipTests>
        <includes>
            <include>${integration.test.files}</include>
        </includes>
        <forkMode>once</forkMode>
        <!--
                            <reuseForks>false</reuseForks>
                            <forkCount>1</forkCount>
        -->
    </configuration>
    <executions>
        <execution>
            <id>integration-test</id>
            <goals>
                <goal>integration-test</goal>
            </goals>
        </execution>
        <execution>
            <id>verify</id>
            <goals>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Now you need just mvn test for your unit tests and mvn verify -Pintegration for your integration tests. Obviously you should create the database.properties files in the specified (on the profiles) paths (or elsewhere and change the paths)

Based-on reference: http://www.petrikainulainen.net/programming/tips-and-tricks/creating-profile-specific-configuration-files-with-maven/

Solution 17 - Java

I found 2 possibilities without changing classloader/using other Maven plugins/profiles/copy-overwrite files.

TL;DR: check provider name.

At first I started to construct the entityManagerFactory programmatically, like here: https://stackoverflow.com/questions/29434121/create-entity-manager-programmatically-without-persistence-file#answer-42372934.

So I did sth very similar:

    @BeforeClass
    public static void prepare() {
        Map<String, Object> configOverrides = new HashMap<>();
        configOverrides.put("hibernate.connection.driver_class", "org.h2.Driver");
        configOverrides.put("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
        configOverrides.put("hibernate.connection.username", "sa");
        configOverrides.put("hibernate.connection.password", "sa");
        configOverrides.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        configOverrides.put("hibernate.show_sql", "true");
        configOverrides.put("hibernate.hbm2ddl.auto", "validate");
        factory = new HibernatePersistence().createContainerEntityManagerFactory(
                new CustomPersistenceUnitInfo(), configOverrides
        );
        //factory = Persistence.createEntityManagerFactory("test");
        assertNotNull(factory);
    }
...
    private static class CustomPersistenceUnitInfo implements PersistenceUnitInfo {

        @Override
        public String getPersistenceUnitName() {
            return "test";
        }

        @Override
        public String getPersistenceProviderClassName() {
            return "org.hibernate.jpa.HibernatePersistenceProvider";
 // <------------note here: this is wrong!
        }

        @Override
        public PersistenceUnitTransactionType getTransactionType() {
            return PersistenceUnitTransactionType.RESOURCE_LOCAL;
        }

        @Override
        public DataSource getJtaDataSource() {
            return null;
        }

        @Override
        public DataSource getNonJtaDataSource() {
            return null;
        }

        @Override
        public List<String> getMappingFileNames() {
            return Collections.emptyList();
        }

        @Override
        public List<URL> getJarFileUrls() {
            try {
                return Collections.list(this.getClass()
                        .getClassLoader()
                        .getResources(""));
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public URL getPersistenceUnitRootUrl() {
            return null;
        }

        @Override
        public List<String> getManagedClassNames() {
            return Arrays.asList(
                    "com.app.Entity1",
                    "com.app.Entity2"
            );
        }

        @Override
        public boolean excludeUnlistedClasses() {
            return true;
        }

        @Override
        public SharedCacheMode getSharedCacheMode() {
            return null;
        }

        @Override
        public ValidationMode getValidationMode() {
            return null;
        }

        @Override
        public Properties getProperties() {
            return null;
        }

        @Override
        public String getPersistenceXMLSchemaVersion() {
            return null;
        }

        @Override
        public ClassLoader getClassLoader() {
            return null;
        }

        @Override
        public void addTransformer(final ClassTransformer classTransformer) {

        }

        @Override
        public ClassLoader getNewTempClassLoader() {
            return null;
        }
    }


But then, I found it still return null. Why?

Then I found that when I use com.hibernate.ejb.HibernatePersistence class, the provider should not be com.hibernate.jpa.HibernatePersistenceProvider, but com.hibernate.ejb.HibernatePersistence. The class HibernatePersistenceProvider is not even found with IDEA "Open Class", even when it is in the main persistence.xml.

In Ejb3Configuration.class I found:

        integration = integration != null ? Collections.unmodifiableMap(integration) : CollectionHelper.EMPTY_MAP;
        String provider = (String)integration.get("javax.persistence.provider");
        if (provider == null) {
            provider = info.getPersistenceProviderClassName();
        }

        if (provider != null && !provider.trim().startsWith(IMPLEMENTATION_NAME)) { // private static final String IMPLEMENTATION_NAME = HibernatePersistence.class.getName(); which, is, "com.hibernate.ejb.HibernatePersistence"
            LOG.requiredDifferentProvider(provider);
            return null;
        } else {

So I went back to the first solution of persistence.xml, and change provider name, and now it works. It seems that even the provider in main is jpa.xxx, in tests it is not.

So, in summary, 3 things to check:

  • turn on -X in Maven to check if maven-resources-plugin really copied your src/test/resources/META-INF/persistence.xml into target/test-classes(I think this never fails)
  • check if hibernate-entitymanager is in your classpath(you can check with mvn dependency:tree -Dincludes=org.hibernate:hibernate-entitymanager.
  • check provider's name, most important one. Should be org.hibernate.ejb.HibernatePersistence.
<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="test" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>com.app.model.Company</class>
        ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeter BeckerView Question on Stackoverflow
Solution 1 - JavaRich SellerView Answer on Stackoverflow
Solution 2 - JavaArjanView Answer on Stackoverflow
Solution 3 - JavaPeter BeckerView Answer on Stackoverflow
Solution 4 - JavaVlad-HCView Answer on Stackoverflow
Solution 5 - Javad.marzoView Answer on Stackoverflow
Solution 6 - JavaluzzyView Answer on Stackoverflow
Solution 7 - JavaKaiView Answer on Stackoverflow
Solution 8 - JavaTRFView Answer on Stackoverflow
Solution 9 - JavaRobinView Answer on Stackoverflow
Solution 10 - JavaHubert GrzeskowiakView Answer on Stackoverflow
Solution 11 - JavamacbutchView Answer on Stackoverflow
Solution 12 - JavaSteve HighamView Answer on Stackoverflow
Solution 13 - JavaMilankaView Answer on Stackoverflow
Solution 14 - JavasiybView Answer on Stackoverflow
Solution 15 - JavabalmasterView Answer on Stackoverflow
Solution 16 - JavaChrisView Answer on Stackoverflow
Solution 17 - JavaWesternGunView Answer on Stackoverflow