当前位置:Java -> Querydsl vs. JPA Criteria,第5部分:Maven集成

Querydsl vs. JPA Criteria,第5部分:Maven集成

由于大多数技术或依赖性都在迅速发展,有时很难进行初始设置或平稳升级。本文的目标是根据使用的技术,为Querydsl框架提供Maven设置的摘要。之后,让我们简要了解Querydsl解决方案。

本文您将学习到

  • 如何在Spring Boot 2.x(即Java EE)和Spring Boot 3.x(即Jakarta EE)中设置Querydsl
  • 什么是Maven分类器
  • Maven分类器在Querydsl构建中的使用
  • 使用Eclipse Transformer插件

Querydsl设置

在Spring Boot应用程序中设置Querydsl框架有几种可能性。正确的方法取决于所使用的技术。

在深入讨论之前,让我们从推荐的官方设置开始。

官方设置

Querydsl框架有一个很好的文档网站。在第2.1.1章节中描述了Maven集成,其中推荐的设置基于以下内容:

  • querydsl-jpaquerydsl-apt依赖项以及
  • 使用apt-maven-plugin插件。

querydsl-apt依赖项没有在官方网站上提到,但用于生成元数据Q类(请参阅元数据文章)。如果不使用querydsl-apt依赖项,则会出现以下错误:

[INFO] --- apt:1.1.3:process (default) @ sat-jpa ---
error: Annotation processor 'com.querydsl.apt.jpa.JPAAnnotationProcessor' not found
1 error


根据官方推荐的完整工作Maven设置如下所示:

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


该设置有效,Maven构建成功(请查看下面的日志)。不幸的是,其中可以发现几个错误。在我们的情况下,日志包含例如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] ------------------------------------------------------------------------


让我们专注于如何在接下来的部分解决此问题。

针对Spring Boot 2.x的Java EE设置

当我发现这个StackOverflow问题时,我意识到不再需要使用querydsl-apt插件。诀窍在于使用带有jpa分类器的querydsl-apt依赖而不是使用apt-maven-plugin插件。

注意:自Querydsl 3起,querydsl-apt插件似乎已被弃用(请查看以下)。

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


注意:一旦我们指定分类器,我们还需要指定依赖项的版本。因此,我们不能再依赖Spring Boot中定义的版本。

现在Maven构建的日志是干净的。

[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] ------------------------------------------------------------------------


针对Spring Boot 3.x的Jakarta设置

Java EE规范,我们需要略微调整我们的Maven设置。更改已在升级至Spring Boot 3.0中描述。本文基于jakarta分类器代替jpa分类器。
<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>


Maven分类器

Querydsl解决方案

Q类,以便能够轻松编写查询。querydsl-apt依赖项通过JPAAnnotationProcessor的实例实现此目的(有关注解处理的更多详细信息,请参见例如Java注解处理和创建构建器)。注解处理器的确切实现取决于所使用的技术。所需的处理器在位于所使用的querydsl-apt依赖项中的javax.annotation.processing.Processor文件中定义。此文件的内容必须包含所需注解处理器的完整类路径,例如com.querydsl.apt.jpa.JPAAnnotationProcessor
  • 用于旧的Java EE(带有javax.persistence包)的jpa分类器;
  • 用于新的Jakarta EE(带有jakarta.persistence包)的jakarta分类器,如您已经了解的那样。

Maven分类器的目的

Maven分类器的目的如官方网站所解释:

分类器用于区分从同一POM构建的但内容不同的构件。它是一个可选的任意字符串,如果存在,则会在版本号后面直接附加到构件名称上。

以此元素为例,考虑一个项目,它提供一个针对Java 11的构件,同时也提供一个仍支持Java 1.8的构件。第一个构件可以配备分类器jdk11,第二个可以配备jdk8,这样客户端就可以选择使用哪个。

有关Maven分类器使用的更多信息,请参阅这个指南

在我们的案例中,querydsl-apt依赖的所有可用分类器如下所示。它们也可以在线列出这里。同样的,你也可以在querydsl-jpa依赖中看到所有分类器这里

所有分类器列表

当从querydsl-apt-5.0.0-jpa.jarquerydsl-apt-5.0.0-jakarta.jar依赖中反编译com.querydsl.apt.jpa.JPAAnnotationProcessor类时,我们可以看到唯一的差异(见下文)是使用的导入内容(见第5-11行)。结果,JPAAnnotationProcessor能够处理我们类中的不同注释(见第16-20行)。

querydsl-apt-5.0.0-jakarta.jar

使用Maven分类器

Querydsl支持的所有Maven分类器都在pom.xml文件的descriptors元素中定义(见第11-20行):

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


该配置用于根据描述构建多个JAR(见上文)。每个描述符都定义了特定技术的所有细节。通常,XML描述符只是指定了源文件夹(见jpa.xml中的第11行)。

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


然而,Jakarta EE的定义有点复杂。在jakarta.xml中的关键部分是JAR的解压缩(见第16行)并使用jakarta分类器(见第18行)以激活Eclipse Transformer插件。

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


注:在id元素中的值被用作分类器,见这里

Eclipse Transformer插件

org.eclipse.transformer.maven插件。

Eclipse Transformer提供了转换Java二进制文件的工具和运行时组件,例如单个类文件和完整的JAR和WAR,将更改映射到Java包、类型名称和相关资源名称。

org.eclipse.transformer.maven插件在querydsl-apt依赖的第171-187行中定义为:
<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>


注:有关Eclipse Transformer插件的更多信息,请参见此处的博客。

结论

免责声明:本文基于我在尝试解决问题时的调查。如果有任何不准确或误导性的信息,请告诉我。

推荐阅读: 13.JVM的内存分配与回收

本文链接: Querydsl vs. JPA Criteria,第5部分:Maven集成