When I create a composite persistence unit and try to use the criteria API I receive the following exception message:
java.lang.IllegalArgumentException: No [EntityType] was found for the key class [nl.example.application.datalayer.entity.db.EntityA] in the Metamodel - please verify that the [Entity] class was referenced in persistence.xml using a specific <class>nl.example.application.datalayer.entity.db.EntityA</class> property or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element.
If I execute a jpql query, I get the results I expect. For some reason the metamodel remains empty as shown by Warning in the server.log:
The collection of metamodel [EntityType] types is empty. Model classes may not have been found during entity search for Java SE and some Java EE container managed persistence units. Please verify that your entity classes are referenced in persistence.xml using either <class> elements or a global <exclude-unlisted-classes>false</exclude-unlisted-classes> element. The lookup on [class nl.example.application.datalayer.entity.db.EntityA] will return null.
I have created a stripped down version of my application to show the problem, which can be found here. A unit test of the datalayer (which contains the composite persistence unit) does give correct results when I use the criteria API, see CompositeDAOTest in the above mentioned project.
The example project is structured as follows:
Example
|--- datalayer_project)
| |--- datalayer_parent
| | |--- pom.xml
| |
| |--- entity (some jar with entities)
| | |--- src
| | | |---main
| | | |--- java
| | | |--- resources
| | | |--- META-INF/persistence.xml
| | |--- pom.xml
| |
| |--- datalayer (the composite persistence unit to be used by the application)
| | |--- src
| | | |---main
| | | |--- java
| | | |--- resources
| | | |--- META-INF/persistence.xml
| | |--- build-jar-with-dependencies.xml (maven-assembly-plugin descriptor to build jar with all entites and their metamodel classes)
| | |--- pom.xml
| |--- pom.xml
|
|--- application (this is the example application which will be deployed)
| |--- src
| | |--- <source code>
| |--- pom.xml
|
|--- pom.xml
|--- setupServer.sh (script to create GlassFish/Payara domain)
The persistence.xml of the entity jar is defined by
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="examplePU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/DataSource</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>NONE</shared-cache-mode>
<properties>
<property name="eclipselink.composite-unit.member" value="true"/>
<property name="eclipselink.logging.parameters" value="true"/>
<property name="eclipselink.target-database" value="PostgreSQL"/>
<property name="eclipselink.deploy-on-startup" value="true" />
</properties>
</persistence-unit>
That of the composite persistence unit (datalayer) by:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="CompositePu" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jar-file>entity-${project.version}.jar</jar-file>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.composite-unit" value="true" />
<property name="eclipselink.logging.parameters" value="true" />
<property name="eclipselink.deploy-on-startup" value="true" />
</properties>
</persistence-unit>
</persistence>
The project's main deployable is a war, which includes a fat jar containing the composite persistence unit and the entities with their metamodel classes. The pom which creates this application:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>nl.example.application</groupId>
<artifactId>application_project</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>example</artifactId>
<packaging>war</packaging>
<name>example</name>
<dependencies>
<dependency>
<groupId>nl.example.application.datalayer</groupId>
<artifactId>datalayer</artifactId>
<version>${project.version}</version>
<classifier>jar-with-dependencies</classifier>
</dependency>
<dependency>
<groupId>de.danielbechler</groupId>
<artifactId>java-object-diff</artifactId>
<version>0.94</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>6.0</version>
</dependency>
<dependency>
<groupId>org.primefaces.themes</groupId>
<artifactId>all-themes</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.0.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.25.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-core</artifactId>
<version>2.4.3.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se</artifactId>
<version>2.4.3.Final</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>example</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<classpathPrefix>example/WEB-INF/lib</classpathPrefix>
</manifest>
<manifestEntries>
<git-SHA-1>${buildNumber}</git-SHA-1>
</manifestEntries>
</archive>
<failOnMissingWebXml>false</failOnMissingWebXml>
<webResources>
<webResource>
<directory>${basedir}/src/main/webapp</directory>
<filtering>true</filtering>
<includes>
<include>index.xhtml</include>
</includes>
</webResource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sonar-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/business/util/control/Settings**</exclude>
<exclude>**/*_*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<scm>
<connection>scm:git:git@github.com:JCGreiner/JPACompositeAndMetaModel.git</connection>
<tag>HEAD</tag>
</scm>
</project>
In order to easily demonstrate the problem, I have created 3 REST endpoints (store entity, findEntityNative (should have been findEntityJPQL), and findEntityCriteria) in the application which are documented using swagger so they can be executed easily.
The steps to use the project (also found on project wiki here):
- Clone, or download the repository
- Run mvn clean install
- Create a database user in postgres: user: example password: example databasename: example the login role should have sufficient rights to create and modify tables; I used a superuser for convenience
- Setup a GlassFish/Payara domain: Run the setupServer.sh script in the root of the project with 3 arguments: a. path to GlassFish/Payara b. the domain name, e.g. example c. the portbase, e.g. 8000
- Once the domain has been created, go to the admin console (e.g. localhost:8048) and deploy the application. the war is located at /nl/example/application/example/1.0.0-SNAPSHOT/example-1.0.0-SNAPSHOT
- Run the DDLGenerateIT test to generate the database tables
- You can now test the application by going to the swagger documentation found at http://localhost:8080/example/api-docs/index.html here under the default section you will see the 3 REST endpoint, and can easily test them.
Summary (tldr): Application can't find metamodel of composite persistence unit, even though jpql queries work just fine, why?
Tried, but alas no solution:
1. Generate metamodel using processor from hibernate, as suggested in this topic
2. Since different classloaders are used, I tried the following change to CompositeDAO:
@PersistenceContext(unitName = "CompositePu")
protected EntityManager em;
I replaced with:
private EntityManagerFactory emf;
private EntityManager em;
@PostConstruct
public void initialize() {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("eclipselink.classloader", this.getClass().getClassLoader());
hashMap.put("eclipselink.composite-unit", "true");
hashMap.put("eclipselink.deploy-on-startup", "true");
em = Persistence.createEntityManagerFactory("CompositePu", hashMap).createEntityManager();
if (metamodel.getManagedTypes().isEmpty()) {
logger.log(Level.WARNING, "meta model is empty");
}
Unfortunately, the metamodel is still empty, the log line above is triggered.
- This topic about JPA 2.0 suggests to prgrammatically add the class descripters to the metamodel. I have created a quick implementation but alas no success (the class descripters I create aren't complete). However this is probably the most promising way forward to resolve this problem.
Also useful:
1. adding-entity-classes-dynamically-at-runtime