JPQL Criteria MetaModel

In JPA2 there is a query API referred to as "criteria". This is really an API allowing the construction of queries expression by expression, and optionally making it type safe so that if you refactor a field name then it is changed in the queries. One mechanism for allowing refactoring of queries is to have something called a static metamodel of generated classes that mirror the applications persistable classes and have persistable fields marked as public and static so that they can be accessed when generating the queries.



Metamodel Definition

The JPA2 spec contains the following description of the static metamodel.

For every managed class in the persistence unit, a corresponding metamodel class is produced as follows:

  • For each managed class X in package p, a metamodel class X_ in package p is created.
  • The name of the metamodel class is derived from the name of the managed class by appending "_" to the name of the managed class.
  • The metamodel class X_ must be annotated with the javax.persistence.StaticMetamodel annotation
  • If class X extends another class S, where S is the most derived managed class (i.e., entity or mapped superclass) extended by X, then class X_ must extend class S_, where S_ is the meta-model class created for S.
  • For every persistent non-collection-valued attribute y declared by class X, where the type of y is Y, the metamodel class must contain a declaration as follows:
    public static volatile SingularAttribute<X, Y> y;
  • For every persistent collection-valued attribute z declared by class X, where the element type of z is Z, the metamodel class must contain a declaration as follows:
    • if the collection type of z is java.util.Collection, then
      public static volatile CollectionAttribute<X, Z> z;
    • if the collection type of z is java.util.Set, then
      public static volatile SetAttribute<X, Z> z;
    • if the collection type of z is java.util.List, then
      public static volatile ListAttribute<X, Z> z;
    • if the collection type of z is java.util.Map, then
      public static volatile MapAttribute<X, K, Z> z;
      where K is the type of the key of the map in class X

Let's take an example, for the following class

package org.datanucleus.samples.jpa2.metamodel;

import java.util.*;
import javax.persistence.*;

@Entity
public class Person
{
    @Id
    long id;

    String name;

    @OneToMany
    List<Address> addresses;
}

the static metamodel class will be

package org.datanucleus.samples.jpa2.metamodel;

import javax.persistence.metamodel.*;

@StaticMetamodel(Person.class)
public class Person_ 
{
    public static volatile SingularAttribute<Person, Long> id;
    public static volatile SingularAttribute<Person, String> name;
    public static volatile ListAttribute<Person, Address> addresses;
}


Generating the Static Metamodel

DataNucleus provides an annotation processor in the jar datanucleus-jpa-query that can be used when compiling your model classes to generate the static metamodel classes. What this does is when the compile is invoked, all classes that have persistence annotations will be passed to the annotation processor and a Java file generated for its metamodel. Then all classes (original + metamodel) are compiled.

To enable this in Maven2 you would need the above jar, plus datanucleus-core and datanucleus-jpa (as well as persistence-api.jar) in your PM to be in the CLASSPATH at compile

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
    </configuration>
</plugin>


To enable this in Eclipse you would need to do the following

  • Go to Java Compiler and make sure the compiler compliance level is 1.6 or above
  • Go to Java Compiler -> Annotation Processing and enable the project specific settings and enable annotation processing
  • Go to Java Compiler -> Annotation Processing -> Factory Path , enable the project specific settings and then add the following jars to the list: datanucleus-jpa-query.jar , datanucleus-jpa.jar , datanucleus-core.jar