3.3. Code generation

The Java 6 APT annotation processing functionality is used in Querydsl for code generation in the JPA, JDO and Mongodb modules. This section describes various configuration options for the code generation and an alternative to APT usage.

3.3.1. Path initialization

By default Querydsl initializes only reference properties of the first two levels. In cases where longer initialization paths are required, these have to be annotated in the domain types via com.querydsl.core.annotations.QueryInit annotations. QueryInit is used on properties where deep initializations are needed. The following example demonstrates the usage.

@Entity
class Event {
    @QueryInit("customer.address")
    Account account;
}

@Entity
class Account {
    Customer customer;
}

@Entity
class Customer {
    String name;
    Address address;
    // ...
}

This example enforces the initialization of the account.customer path, when an Event path is initialized as a root path / variable. The path initialization format supports wildcards as well, e.g. "customer.*" or just "*".

The automatic path initialization replaces the manual one, which required the entity fields to be non-final. The declarative format has the benefit to be applied to all top level instances of a Query type and to enable the usage of final entity fields.

Automatic path initialization is the preferred initialization strategy, but manual initialization can be activated via the Config annotation, which is described below.

3.3.2. Customization

The serialization of Querydsl can be customized via Config annotations on packages and types. They customize the serialization of the annotated package or type.

The serialization options are

Table 3.1. Config options

NameDescription
entityAccessorsaccessor methods for entity paths instead of public final fields (default: false)
listAccessorslistProperty(int index) style methods (default: false)
mapAccessorsmapProperty(Key key) style accessor methods (default: false)
createDefaultVariablegenerate the default variable (default: true)
defaultVariableNamename of the default variable

Below are some examples.

Customization of Entity type serialization:

@Config(entityAccessors=true)
@Entity
public class User {
    //...
}

Customization of package content:

@Config(listAccessors=true)
package com.querydsl.core.domain.rel;

import com.querydsl.core.annotations.Config;

If you want to customize the serializer configuration globally, you can do this via the following APT options

Table 3.2. APT options

NameDescription
querydsl.entityAccessorsenable reference field accessors
querydsl.listAccessorsenable accessors for direct indexed list access
querydsl.mapAccessorsenable accessors for direct key based map access
querydsl.prefixoverride the prefix for query types(default: Q)
querydsl.suffixset a suffix for query types
querydsl.packageSuffixset a suffix for query type packages
querydsl.createDefaultVariableset whether default variables are created
querydsl.unknownAsEmbeddableset where unknown non-annotated classes should be treated as embeddable (default: false)
querydsl.includedPackagescomma separated list of packages to be included into code generation (default: all)
querydsl.includedClassescomma separated list of class names to be included into code generation (default: all)
querydsl.excludedPackagescomma separated list of packages to be excluded from code generation (default: none)
querydsl.excludedClassescomma separated list of class names to be excluded from code generation (default: none)
querydsl.useFieldsset whether fields are used as metadata source (default: true)
querydsl.useGettersset whether accessors are used as metadata source (default: true)
querydsl.generatedAnnotationClass The fully qualified class name of the Single-Element Annotation (with String element) to be added on the generated sources. Build in com.querydsl.core.annotations.Generatedhas CLASS retention which can be used for byte code analysis tools like Jacoco. (default: javax.annotation.Generated orjavax.annotation.processing.Generated depending on the java version). See also Single-Element Annotation

Using the Maven APT plugin this works for example like this:

<project>
  <build>
  <plugins>
    ...
    <plugin>
      <groupId>com.mysema.maven</groupId>
      <artifactId>apt-maven-plugin</artifactId>
      <version>1.1.3</version>
      <executions>
        <execution>
          <goals>
            <goal>process</goal>
          </goals>
          <configuration>
            <outputDirectory>target/generated-sources/java</outputDirectory>
            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            <options>
              <querydsl.entityAccessors>true</querydsl.entityAccessors>
              <querydsl.useFields>false</querydsl.useFields>
            </options>
          </configuration>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
  </build>
</project>

Alternatively maven-compiler-plugin can be configured to hook APT directly into compilation:

      <project>
        <build>
        <plugins>
          ...
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <generatedSourcesDirectory>target/generated-sources/java</generatedSourcesDirectory>
              <compilerArgs>
                <arg>-Aquerydsl.entityAccessors=true</arg>
                <arg>-Aquerydsl.useFields=false</arg>
              </compilerArgs>
            </configuration>
            <dependencies>
              <dependency>
                <groupId>com.querydsl</groupId>
                <artifactId>querydsl-apt</artifactId>
                <version>${querydsl.version}</version>
                <classifier>jpa</classifier>
              </dependency>
              <dependency>
                <groupId>org.hibernate.javax.persistence</groupId>
                <artifactId>hibernate-jpa-2.1-api</artifactId>
                <version>1.0.0.Final</version>
              </dependency>
            </dependencies>
          </plugin>
          ...
        </plugins>
        </build>
      </project>

Notice that you need to use a proper classifier when defining dependency to com.querydsl:querydsl-apt. Those additional artifacts define the annotation processor to be used in META-INF/services/javax.annotation.processing.Processor.

Available classifiers include:

  • general
  • hibernate
  • jdo
  • jpa

With this configuration query objects can have their sources generated and compiled during compilation of the domain objects. This will also automatically add the generated sources directory to Maven project source roots.

The great advantage of this approach is that it can also handle annotated Groovy classes using groovy-eclipse compiler:

      <project>
        <build>
        <plugins>
          ...
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <compilerId>groovy-eclipse-compiler</compilerId>
              <generatedSourcesDirectory>target/generated-sources/java</generatedSourcesDirectory>
              <compilerArgs>
                <arg>-Aquerydsl.entityAccessors=true</arg>
                <arg>-Aquerydsl.useFields=false</arg>
              </compilerArgs>
            </configuration>
            <dependencies>
              <dependency>
                <groupId>org.codehaus.groovy</groupId>
                <artifactId>groovy-eclipse-compiler</artifactId>
                <version>2.9.1-01</version>
              </dependency>
              <dependency>
                <groupId>org.codehaus.groovy</groupId>
                <artifactId>groovy-eclipse-batch</artifactId>
                <version>2.3.7-01</version>
              </dependency>
              <dependency>
                <groupId>com.querydsl</groupId>
                <artifactId>querydsl-apt</artifactId>
                <version>${querydsl.version}</version>
                <classifier>jpa</classifier>
              </dependency>
              <dependency>
                <groupId>org.hibernate.javax.persistence</groupId>
                <artifactId>hibernate-jpa-2.1-api</artifactId>
                <version>1.0.0.Final</version>
              </dependency>
            </dependencies>
          </plugin>
          ...
        </plugins>
        </build>
      </project>

3.3.3. Custom type mappings

Custom type mappings can be used on properties to override the derived Path type. This can be useful for example in cases where comparison and String operations should be blocked on certain String paths or Date / Time support for custom types needs to be added. Support for Date / Time types of the Joda time API and JDK (java.util.Date, Calendar and subtypes) is built in, but other APIs might need to be supported using this feature.

The following example demonstrates the usage:

@Entity
public class MyEntity {
    @QueryType(PropertyType.SIMPLE)
    public String stringAsSimple;

    @QueryType(PropertyType.COMPARABLE)
    public String stringAsComparable;

    @QueryType(PropertyType.NONE)
    public String stringNotInQuerydsl;
}

The value PropertyType.NONE can be used to skip a property in the query type generation. This case is different from @Transient or @QueryTransient annotated properties, where properties are not persisted. PropertyType.NONE just omits the property from the Querydsl query type.

3.3.4. Delegate methods

To declare a static method as a delegate method add the QueryDelegate annotation with the corresponding domain type as a value and provide a method signature that takes the corresponding Querydsl query type as the first argument.

Here is a simple example from a unit test:

@QueryEntity
public static class User {

    String name;

    User manager;

}
@QueryDelegate(User.class)
public static BooleanPath isManagedBy(QUser user, User other) {
    return user.manager.eq(other);
}

And the generated methods in the QUser query type:

public BooleanPath isManagedBy(QUser other) {
    return DelegateTest.isManagedBy(this, other);
}

Delegate methods can also be used to extend built-in types. Here are some examples

public class QueryExtensions {

    @QueryDelegate(Date.class)
    public static BooleanExpression inPeriod(DatePath<Date> date, Pair<Date,Date> period) {
        return date.goe(period.getFirst()).and(date.loe(period.getSecond()));
    }

    @QueryDelegate(Timestamp.class)
    public static BooleanExpression inDatePeriod(DateTimePath<Timestamp> timestamp, Pair<Date,Date> period) {
        Timestamp first = new Timestamp(DateUtils.truncate(period.getFirst(), Calendar.DAY_OF_MONTH).getTime());
        Calendar second = Calendar.getInstance();
        second.setTime(DateUtils.truncate(period.getSecond(), Calendar.DAY_OF_MONTH));
        second.add(1, Calendar.DAY_OF_MONTH);
        return timestamp.goe(first).and(timestamp.lt(new Timestamp(second.getTimeInMillis())));
    }

}

When delegate methods are declared for builtin types then subclasses with the proper delegate method usages are created:

public class QDate extends DatePath<java.sql.Date> {

    public QDate(BeanPath<? extends java.sql.Date> entity) {
        super(entity.getType(), entity.getMetadata());
    }

    public QDate(PathMetadata<?> metadata) {
        super(java.sql.Date.class, metadata);
    }

    public BooleanExpression inPeriod(com.mysema.commons.lang.Pair<java.sql.Date, java.sql.Date> period) {
        return QueryExtensions.inPeriod(this, period);
    }

}

public class QTimestamp extends DateTimePath<java.sql.Timestamp> {

    public QTimestamp(BeanPath<? extends java.sql.Timestamp> entity) {
        super(entity.getType(), entity.getMetadata());
    }

    public QTimestamp(PathMetadata<?> metadata) {
        super(java.sql.Timestamp.class, metadata);
    }

    public BooleanExpression inDatePeriod(com.mysema.commons.lang.Pair<java.sql.Date, java.sql.Date> period) {
        return QueryExtensions.inDatePeriod(this, period);
    }

}

3.3.5. Non-annotated types

It is possible to create Querydsl query types for non annotated types by creating @QueryEntities annotations. Just place a QueryEntities annotation into a package of your choice and the classes to mirrored in the value attribute.

To actually create the types use the com.querydsl.apt.QuerydslAnnotationProcessor. In Maven you do it like this:

<project>
  <build>
  <plugins>
    ...
    <plugin>
      <groupId>com.mysema.maven</groupId>
      <artifactId>apt-maven-plugin</artifactId>
      <version>1.1.3</version>
      <executions>
        <execution>
          <goals>
            <goal>process</goal>
          </goals>
          <configuration>
            <outputDirectory>target/generated-sources/java</outputDirectory>
            <processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>
          </configuration>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
  </build>
</project>

3.3.6. Classpath based code generation

For cases where annotated Java sources are not available, such as the usage of a different JVM language such as Scala or Groovy or annotation addition via bytecode manipulation the GenericExporter class can be used to scan the classpath for annotated classes and generate query types for them.

To make GenericExporter available add a dependency to the querydsl-codegen module to your project, or to be more precise com.querydsl:querydsl-codegen:${querydsl.version}.

Below is an example for JPA

GenericExporter exporter = new GenericExporter();
exporter.setKeywords(Keywords.JPA);
exporter.setEntityAnnotation(Entity.class);
exporter.setEmbeddableAnnotation(Embeddable.class);
exporter.setEmbeddedAnnotation(Embedded.class);
exporter.setSupertypeAnnotation(MappedSuperclass.class);
exporter.setSkipAnnotation(Transient.class);
exporter.setTargetFolder(new File("target/generated-sources/java"));
exporter.export(DomainClass.class.getPackage());

This will export all the JPA annotated classes in the package of the DomainClass class and subpackages to the target/generated-sources/java directory.

3.3.6.1. Usage via Maven

The goals generic-export, jpa-export and jdo-export of the querydsl-maven-plugin can be used for GenericExporter usage via Maven.

The different goals are mapped to the Querydsl, JPA and JDO annotations.

The configuration elements are

Table 3.3. Maven configuration

TypeElementDescription
FiletargetFoldertarget folder for generated sources
booleanscalatrue, if Scala sources should be generated instead (default: false)
String[]packagespackages to be introspected for entity classes
booleanhandleFieldstrue, if fields should be treated as properties (default: true)
booleanhandleMethodstrue, if getters should be treated as properties (default: true)
StringsourceEncodingcharset encoding for the generated source files
booleantestClasspathtrue, if the test classpath should be used instead

Here is an example for JPA annotated classes

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        <executions>
          <execution>
            <phase>process-classes</phase>
            <goals>
              <goal>jpa-export</goal>
            </goals>
            <configuration>
              <targetFolder>target/generated-sources/java</targetFolder>
              <packages>
                <package>com.example.domain</package>
              </packages>
            </configuration>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  </build>
</project>

This will export the JPA annotated classes of the com.example.domain package and subpackages to the target/generated-sources/java directory.

If you need to compile the generated sources directly after that, then you can use the compile goal for that.

<execution>
  <goals>
    <goal>compile</goal>
  </goals>
  <configuration>
    <sourceFolder>target/generated-sources/scala</targetFolder>
  </configuration>
</execution>

The compile goal has the following configuration elements

Table 3.4. Maven configuration

TypeElementDescription
FilesourceFoldersource folder with generated sources
StringsourceEncodingcharset encoding of sources
Stringsource-source option for the compiler
Stringtarget-target option for the compiler
booleantestClasspathtrue, if the test classpath should be used instead
MapcompilerOptionsoptions for the compiler

All options except sourceFolder are optional.

3.3.6.2. Scala support

If you need Scala output of the classes, use a variant of the following configuration

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        <dependencies>
          <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-scala</artifactId>
            <version>${querydsl.version}</version>
          </dependency>
          <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <goals>
              <goal>jpa-export</goal>
            </goals>
            <configuration>
              <targetFolder>target/generated-sources/scala</targetFolder>
              <scala>true</scala>
              <packages>
                <package>com.example.domain</package>
              </packages>
            </configuration>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  </build>
</project>