1

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):

  1. Clone, or download the repository
  2. Run mvn clean install
  3. 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
  4. 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
  5. 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
  6. Run the DDLGenerateIT test to generate the database tables
  7. 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.

  1. 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

  • Native queries don't require classes to be found. Turn on logging and see what is being found when the persistence unit gets deployed: https://wiki.eclipse.org/EclipseLink/Examples/JPA/Logging Also check that a simple JPQL query works, as it might be a classloading issue where the loader used for the persistence unit isn't the same as used by your application. – Chris May 18 '17 at 16:36
  • Thanks for the suggestion. I have incorrectly written native query, when I meant JPQL query. The query that works is created by the following code: em.createQuery("select e from EntityA e where e.dummyValue = :dummyValue",EntityA.class). The classes are loaded, i.e. in the MetaModelImpl.initialize(ClassLoader classLoader) method in eclipselink, I see that both the application classes as well as the jar containing the entities are loaded. It thus appears not to be a classloading issue. – Charles Greiner May 19 '17 at 09:25
  • MetaModelImpl is from where? I can't find an initialize method that takes a classloader, so I don't know how that would work. Try just createQuery("select e from EntityA") and see, as I don't think the EntityA.class you pass in is from the same loader the persistence unit was loaded with. The classloader can be set in EclipseLink using a property when creating the factory https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_classloader.htm – Chris May 19 '17 at 14:09
  • Indeed they appear to be different classloaders. – Charles Greiner May 19 '17 at 15:17
  • @Chris: I modified the the code in the bean which contains the persistence unit as per your suggestion, which unfortunately does not help. Any further ideas? Thank you for your help! – Charles Greiner May 19 '17 at 15:24
  • You can't just call createEntityManagerFactory as the container will already have loaded the EMF (If you aren't leaving management to the container, you also must manage its lifecycle being sure to close it when done or you'll run into problems). You might try accessing your individual persistence units first and verify they work in your application before trying to wrap them in a CompositePu. according to the example https://wiki.eclipse.org/EclipseLink/Examples/JPA/Composite the composite flag only belongs in the 1 main PU that compases the others - the rest need to be independent. – Chris May 19 '17 at 19:14

0 Answers0