Querydsl vs. JPA Criteria, Part 5: Maven Integration
The first part takes a look at the Maven setup of Querydsl framework with Java EE and Jakarta. The second part sheds light on Querydsl solution with Maven classifiers.
Join the DZone community and get the full member experience.
Join For FreeAs most technologies or dependencies evolve fast, it's sometimes hard to make the initial setup or upgrade smoothly. The goal of this article is to provide a summary of the Maven setup for the Querydsl framework, depending on the used technology. After that, let's see a short overview of the Querydsl solution.
In This Article, You Will Learn
- How to setup Querydsl with Spring Boot 2.x (i.e Java EE) and Spring Boot 3.x (i.e. Jakarta EE)
- What is a Maven classifier
- How is the Maven classifier used in Querydsl build
- Usage of Eclipse Transformer Plugin
Querydsl Setup
There are several possibilities to set up Querydsl framework in a Spring Boot application. The correct approach depends on the technologies used.
Before we get into it, let's start with the recommended official setup.
Official Setup
Querydsl framework has a nice documentation site. The Maven integration is described in Chapter 2.1.1 where the recommended setup is based on the following:
querydsl-jpa
andquerydsl-apt
dependencies and- usage of
apt-maven-plugin
plugin.
The querydsl-apt
dependency isn't mentioned on the official site, but such dependency is needed for the generation of metadata Q
classes (see Metadata article). If we don't use querydsl-apt
dependency then we get the error like this:
[INFO] --- apt:1.1.3:process (default) @ sat-jpa ---
error: Annotation processor 'com.querydsl.apt.jpa.JPAAnnotationProcessor' not found
1 error
The full working Maven setup based on the official recommendation is like this:
<dependencies>
...
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
...
</dependencies>
<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>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
This setup is working, and the Maven build is successful (see the log below). Unfortunately, several errors can be found there. In our case, the logs contain e.g. error: cannot find symbol import static com.github.aha.sat.jpa.city.City_.COUNTRY
.
[INFO] ---------------------< com.github.aha.sat:sat-jpa >---------------------
[INFO] Building sat-jpa 0.5.2-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ sat-jpa ---
[INFO] Deleting <local_path>\sat-jpa\target
[INFO]
[INFO] --- apt:1.1.3:process (default) @ sat-jpa ---
<local_path>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityRepository.java:3: error: cannot find symbol
import static com.github.aha.sat.jpa.city.City_.COUNTRY;
^
symbol: class City_
location: package com.github.aha.sat.jpa.city
<local_path>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityRepository.java:3: error: static import only from classes and interfaces
import static com.github.aha.sat.jpa.city.City_.COUNTRY;
^
<local_path>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityRepository.java:4: error: cannot find symbol
import static com.github.aha.sat.jpa.city.City_.NAME;
^
symbol: class City_
location: package com.github.aha.sat.jpa.city
...
19 errors
[INFO]
[INFO] --- resources:3.2.0:resources (default-resources) @ sat-jpa ---
...
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 51, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.2.2:jar (default-jar) @ sat-jpa ---
[INFO] Building jar: <local_path>\sat-jpa\target\sat-jpa.jar
[INFO]
[INFO] --- spring-boot:2.7.5:repackage (repackage) @ sat-jpa ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15.680 s
[INFO] Finished at: 2023-09-20T08:43:59+02:00
[INFO] ------------------------------------------------------------------------
Let's focus on how to fix this issue in the next parts.
Setup for Java EE With Spring Boot 2.x
Once I found this StackOverflow issue, I realized that the querydsl-apt
plugin is no longer needed. The trick lies in using querydsl-apt
a dependency with a jpa
classifier instead of using the apt-maven-plugin
plugin.
Note: the querydsl-apt
plugin seems to be deprecated since Querydsl 3 (see the following).
With that, the simplified Maven setup looks like this:
<dependencies>
...
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jpa</classifier>
<scope>provided</scope>
</dependency>
...
</dependencies>
Note: Once we specify the classifier, we also need to specify a version of the dependency. Therefore, we cannot rely on the version defined in Spring Boot anymore.
The logs from the Maven build are clean now.
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< com.github.aha.sat:sat-jpa >---------------------
[INFO] Building sat-jpa 0.5.2-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ sat-jpa ---
[INFO] Deleting <local_path>\sat-jpa\target
[INFO]
[INFO] --- resources:3.2.0:resources (default-resources) @ sat-jpa ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 2 resources
...
[INFO]
[INFO] --- jar:3.2.2:jar (default-jar) @ sat-jpa ---
[INFO] Building jar: <local_path>\sat-jpa\target\sat-jpa.jar
[INFO]
[INFO] --- spring-boot:2.7.5:repackage (repackage) @ sat-jpa ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15.148 s
[INFO] Finished at: 2023-09-20T08:56:42+02:00
[INFO] ------------------------------------------------------------------------
Setup for Jakarta With Spring Boot 3.x
As Spring Boot 3 relies on Jakarta instead of Java EE specification, we need to adjust our Maven setup a little bit. The change is described in Upgrade to Spring Boot 3.0. This article is based on this.
Basically, we just need to use jakarta
classifier instead of jpa
classifier.
<dependencies>
...
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
<scope>provided</scope>
</dependency>
...
</dependencies>
The maven build output is the same as in the Java EE setup.
Maven Classifiers
Let's shed light on the Querydsl solution once we understand Maven classifier usage.
Querydsl Solution
Querydsl generates metadata Q
classes for every entity in order to be able to write queries easily. The querydsl-apt
dependency achieves it with an instance of JPAAnnotationProcessor
(see e.g., Java Annotation Processing and Creating a Builder for more details on annotation processing). The exact implementation of the annotation processor depends on the technology used. The desired processor is defined in javax.annotation.processing.Processor file located in the used querydsl-apt
dependency. The content of this file has to have the full classpath to the desired annotation processor, e.g. com.querydsl.apt.jpa.JPAAnnotationProcessor
.
Let's go back to the classifiers for a while. Querydsl supports several classifiers (e.g., JPA, jdo, roo, etc.), and each of them needs a different treatment based on the used technology. Therefore, Querydsl needs to specify the supported annotations for each technology.
For JPA, Querydsl supports these classifiers:
jpa
classifier for the old Java EE (withjavax.persistence
package) andjakarta
classifier for a new Jakarta EE (withjakarta.persistence
package) as you already know.
Purpose of Maven Classifier
The purpose of the Maven classifier is explained on the official site as follows:
The classifier distinguishes artifacts that were built from the same POM but differ in content. It is some optional and arbitrary string that — if present — is appended to the artifact name just after the version number.
As a motivation for this element, consider for example a project that offers an artifact targeting Java 11 but at the same time also an artifact that still supports Java 1.8. The first artifact could be equipped with the classifier
jdk11
and the second one withjdk8
such that clients can choose which one to use.
Please check, e.g., this guide for more information about the Maven classifier usage.
In our case, all the available classifiers for querydsl-apt
dependency are depicted below. They can also be listed online here. Similarly, you can also see all the classifiers for querydsl-jpa
dependency here.
When com.querydsl.apt.jpa.JPAAnnotationProcessor
class is de-compiled from querydsl-apt-5.0.0-jpa.jar
and querydsl-apt-5.0.0-jakarta.jar
dependencies then we can see the only difference (see depicted below) is in the used imports (see lines 5-11). As a result, the JPAAnnotationProcessor is capable of handling different annotations in our classes (see lines 16-20).
Use of Maven Classifier
All Maven classifiers supported by Querydsl are defined in descriptors
element specified in pom.xml file (see lines 11-20) as:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>apt-jars</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/general.xml</descriptor>
<descriptor>src/main/hibernate.xml</descriptor>
<descriptor>src/main/jdo.xml</descriptor>
<descriptor>src/main/jpa.xml</descriptor>
<descriptor>src/main/jakarta.xml</descriptor>
<descriptor>src/main/morphia.xml</descriptor>
<descriptor>src/main/roo.xml</descriptor>
<descriptor>src/main/onejar.xml</descriptor>
</descriptors>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
This configuration is used in order to build multiple JARs defined by descriptors (see above). Each descriptor defines all the specifics for the technology. Usually, the XML descriptor just specifies the source folder (see line 11 in jpa.xml).
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>jpa</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/apt/jpa</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
However, the definition of Jakarta EE is a little bit more complicated. The key part in jakarta.xml is unpacking of JAR (see line 16) and using the jakarta
classifier (see line 18) in order to activate the Eclipse Transformer Plugin.
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>jakarta</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<moduleSets>
<moduleSet>
<useAllReactorProjects>true</useAllReactorProjects>
<includes>
<include>${project.groupId}:${project.artifactId}</include>
</includes>
<binaries>
<unpack>true</unpack>
<includeDependencies>false</includeDependencies>
<attachmentClassifier>jakarta</attachmentClassifier>
<outputDirectory>/</outputDirectory>
</binaries>
</moduleSet>
</moduleSets>
<fileSets>
<fileSet>
<directory>src/apt/jpa</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Note: the value in the id
element is used as the classifier, see here.
Eclipse Transformer Plugin
The last piece in the puzzle lies in the usage of the already-mentioned org.eclipse.transformer.maven
plugin.
Eclipse Transformer provides tools and runtime components that transform Java binaries, such as individual class files and complete JARs and WARs, mapping changes to Java packages, type names, and related resource names.
The org.eclipse.transformer.maven
plugin is defined on lines 171-187 in querydsl-apt dependency as:
<plugin>
<groupId>org.eclipse.transformer</groupId>
<artifactId>org.eclipse.transformer.maven</artifactId>
<version>0.2.0</version>
<executions>
<execution>
<id>jakarta-ee</id>
<goals>
<goal>run</goal>
</goals>
<phase>package</phase>
<configuration>
<classifier>jakarta</classifier>
</configuration>
</execution>
</executions>
</plugin>
Note: See this blog for more information about Eclipse Transformer plugin usage.
Conclusion
This article has covered Querydsl setups for Java EE and Jakarta EE. The rest explained the usage of the Maven classifier by Querydsl.
The used source code (even though it wasn't a lot here) is available here.
Disclaimer: The article is based on my investigation when I tried to figure out the solution. Please let me know of any inaccuracies or misleading information.
Opinions expressed by DZone contributors are their own.
Comments