Querydsl 參考指南


英文版原文Querydsl Reference Guide
全文已翻譯完成,但由於博主精力有限,難免有錯漏之處,請見諒(錯漏問題可以記錄在評論區)

目錄

前言

Querydsl 是一個框架,可以構建靜態類型的 SQL 類查詢。無需將查詢編寫為內聯字符串或將它們外部化為 XML 文件,它們可以通過 Querydsl 之類的流暢 API 構建。

與簡單字符串相比,使用 fluent API 的好處是:

  • IDE 中的代碼完成
  • 幾乎不允許語法上無效的查詢
  • 可以安全地引用域類型和屬性
  • 更好地重構領域類型的變化

1. 簡介

1.1. 背景

Querydsl 的誕生是出於以類型安全的方式維護 HQL 查詢的需要。HQL 查詢的增量構造需要字符串連接,導致代碼難以閱讀。通過純字符串對域類型和屬性的不安全引用是基於字符串的 HQL 構造的另一個問題。

隨着領域模型的變化,類型安全為軟件開發帶來了巨大的好處。域更改直接反映在查詢中,查詢構造中的自動完成使查詢構造更快、更安全。

Hibernate 的 HQL 是 Querydsl 的第一個目標語言,但現在它支持 JPA、JDO、JDBC、Lucene、Hibernate Search、MongoDB、Collections 和 RDFBean 作為后端。

1.2. 原則

類型安全是 Querydsl 的核心原則。查詢是根據生成的查詢類型構建的,這些查詢類型反映了您的域類型的屬性。函數/方法調用也以完全類型安全的方式構造。

一致性是另一個重要原則。查詢路徑和操作在所有實現中都是相同的,查詢接口也有一個通用的基本接口。

要了解 Querydsl 查詢和表達式類型的表達能力,請訪問 javadocs 並探索 com.querydsl.core.Querycom.querydsl.core.Fetchablecom.querydsl.core.types.Expression

2. 教程

我們提供 Querydsl 主要后端的集成指南,而不是一般的入門指南。

2.1. 查詢 JPA

Querydsl 定義了一種通用的靜態類型語法,用於在持久的域模型數據之上進行查詢。JDO 和 JPA 是 Querydsl 的主要集成技術。本指南描述了如何將 Querydsl 與 JPA 結合使用。

JPA 的 Querydsl 是 JPQL 和 Criteria 查詢的替代方案。它以完全類型安全的方式結合了 Criteria 查詢的動態特性和 JPQL 的表現力以及所有這些。

2.1.1. Maven 集成

將以下依賴項添加到您的 Maven 項目中:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

現在,配置 Maven APT 插件:

<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>
          </configuration>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
  </build>
</project>

JPAAnnotationProcessor 查找使用 javax.persistence.Entity 注釋注釋的域類型並為它們生成查詢類型。

如果你在你的域類型中使用 Hibernate 注釋,你應該使用 APT 處理器 com.querydsl.apt.hibernate.HibernateAnnotationProcessor 代替。

運行全新安裝,您將在 target/generated-sources/java 中生成查詢類型。

如果您使用 Eclipse,請運行 mvn eclipse:eclipse 以更新您的 Eclipse 項目以包含 target/generated-sources/java 作為源文件夾。

現在您可以構建 JPA 查詢實例和查詢域模型的實例。

2.1.2. 螞蟻集成

將來自 full-deps 包的 jar 文件放在類路徑中,並使用以下任務生成 Querydsl 代碼:

    <!-- 基於 APT 的代碼生成 -->
    <javac srcdir="${src}" classpathref="cp">
      <compilerarg value="-proc:only"/>
      <compilerarg value="-processor"/>
      <compilerarg value="com.querydsl.apt.jpa.JPAAnnotationProcessor"/>
      <compilerarg value="-s"/>
      <compilerarg value="${generated}"/>
    </javac>

    <!-- 編譯 -->
    <javac classpathref="cp" destdir="${build}">
      <src path="${src}"/>
      <src path="${generated}"/>
    </javac>

src 替換為您的主源文件夾,將 generated 替換為生成的源文件夾,將 build 替換為目標文件夾。

2.1.3. 在 Roo 中使用 Querydsl JPA

如果您將 Querydsl JPA 與 Spring Roo 一起使用,您可以將 com.querydsl.apt.jpa.JPAAnnotationProcessor 替換為 com.querydsl.apt.roo.RooAnnotationProcessor,它將處理 @RooJpaEntity@RooJpaActiveRecord 注釋類@Entity 注釋類。

基於 APT 的代碼生成不適用於 AspectJ IDT。

2.1.4. 從 hbm.xml 文件生成模型

如果您使用帶有基於 XML 的配置的 Hibernate,您可以使用 XML 元數據來創建您的 Querydsl 模型。

com.querydsl.jpa.codegen.HibernateDomainExporter 提供了以下功能:

HibernateDomainExporter exporter = new HibernateDomainExporter(
  "Q",                     // 名稱前綴
  new File("target/gen3"), // 目標文件夾
  configuration);          // org.hibernate.cfg.Configuration 的實例

exporter.export();

HibernateDomainExporter 需要在域類型可見的類路徑中執行,因為屬性類型是通過反射解析的。

所有 JPA 注釋都將被忽略,但會考慮 @QueryInit 和 @QueryType 等 Querydsl 注釋。

2.1.5. 使用查詢類型

要使用 Querydsl 創建查詢,您需要實例化變量和 Query 實現。我們將從變量開始。

假設您的項目具有以下域類型:

@Entity
public class Customer {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setFirstName(String fn) {
        firstName = fn;
    }

    public void setLastName(String ln) {
        lastName = ln;
    }
}

Querydsl 將生成一個簡單名稱為 QCustomer 的查詢類型到與 Customer 相同的包中。QCustomer 可以用作 Querydsl 查詢中的靜態類型變量,作為 Customer 類型的代表。

QCustomer 有一個可以作為靜態字段訪問的默認實例變量:

QCustomer customer = QCustomer.customer;

或者,您可以像這樣定義自己的客戶變量:

QCustomer customer = new QCustomer("myCustomer");

2.1.6. 查詢

Querydsl JPA 模塊同時支持 JPA 和 Hibernate API。

要使用 JPA API,您可以使用 JPAQuery 實例進行查詢,如下所示:

// 其中 entityManager 是 JPA EntityManager
JPAQuery<?> query = new JPAQuery<Void>(entityManager);

如果您使用的是 Hibernate API,則可以像這樣實例化一個 HibernateQuery

// 其中 session 是一個 Hibernate 會話
HibernateQuery<?> query = new HibernateQuery<Void>(session);

JPAQueryHibernateQuery 都實現了 JPQLQuery 接口。

對於本章的示例,查詢是通過“JPAQueryFactory”實例創建的。JPAQueryFactory 應該是獲取 JPAQuery 實例的首選選項。

對於 Hibernate API HibernateQueryFactory 可以使用

要檢索名字為 Bob 的客戶,您將構建如下查詢:

QCustomer customer = QCustomer.customer;
Customer bob = queryFactory.selectFrom(customer)
  .where(customer.firstName.eq("Bob"))
  .fetchOne();

selectFrom 調用定義了查詢源和投影,where 部分定義了過濾器,而 fetchOne 告訴 Querydsl 返回單個元素。容易,對吧?

要創建具有多個來源的查詢,您可以使用如下查詢:

QCustomer customer = QCustomer.customer;
QCompany company = QCompany.company;
query.from(customer, company);

並使用多個過濾器像這樣使用它

queryFactory.selectFrom(customer)
    .where(customer.firstName.eq("Bob"), customer.lastName.eq("Wilson"));

或者像這樣

queryFactory.selectFrom(customer)
    .where(customer.firstName.eq("Bob").and(customer.lastName.eq("Wilson")));

在原生 JPQL 形式中,查詢將這樣編寫:

select customer from Customer as customer
where customer.firstName = "Bob" and customer.lastName = "Wilson"

如果您想通過“或”組合過濾器,請使用以下模式

queryFactory.selectFrom(customer)
    .where(customer.firstName.eq("Bob").or(customer.lastName.eq("Wilson")));

2.1.7. 使用聯結

Querydsl 在 JPQL 中支持以下連接變體:內聯結、聯結、左聯結和右聯結。連接使用是類型安全的,並遵循以下模式:

QCat cat = QCat.cat;
QCat mate = new QCat("mate");
QCat kitten = new QCat("kitten");
queryFactory.selectFrom(cat)
    .innerJoin(cat.mate, mate)
    .leftJoin(cat.kittens, kitten)
    .fetch();

查詢的原生 JPQL 版本將是

select cat from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten

另一個例子

queryFactory.selectFrom(cat)
    .leftJoin(cat.kittens, kitten)
    .on(kitten.bodyWeight.gt(10.0))
    .fetch();

使用以下 JPQL 版本

select cat from Cat as cat
left join cat.kittens as kitten
on kitten.bodyWeight > 10.0

2.1.8. 一般用法

像這樣使用 JPQLQuery 接口的級聯方法

select: 設置查詢的投影。(如果通過查詢工廠創建則不需要)

from: 在此處添加查詢源。

innerJoin, join, leftJoin, rightJoin, on: 使用這些結構添加連接元素。對於連接方法,第一個參數是連接源,第二個是目標(別名)。

where: 添加查詢過濾器,以可變參數形式通過逗號分隔或通過 and 運算符級聯。

groupBy: 以可變參數形式添加分組參數。

have: 添加具有“group by”分組的過濾器作為謂詞表達式的 varags 數組。

orderBy: 將結果的順序添加為順序表達式的可變參數數組。對數字、字符串和其他可比較的表達式使用 asc() 和 desc() 來訪問 OrderSpecifier 實例。

limit,offset,restrict: 設置結果的分頁。最大結果的限制,跳過行的偏移量和一次調用中定義兩者的限制。

2.1.9. Ordering

聲明 Ordering 的語法是

QCustomer customer = QCustomer.customer;
queryFactory.selectFrom(customer)
    .orderBy(customer.lastName.asc(), customer.firstName.desc())
    .fetch();

相當於下面的原生 JPQL

select customer from Customer as customer
order by customer.lastName asc, customer.firstName desc

2.1.10. Grouping

Grouping 可以按以下形式進行

queryFactory.select(customer.lastName).from(customer)
    .groupBy(customer.lastName)
    .fetch();

相當於下面的原生 JPQL

select customer.lastName
from Customer as customer
group by customer.lastName

2.1.11. Delete 子句

Querydsl JPA 中的刪除子句遵循簡單的 delete-where-execute 形式。這里有些例子:

QCustomer customer = QCustomer.customer;
//刪除所有客戶
queryFactory.delete(customer).execute();
// 刪除所有等級小於 3 的客戶
queryFactory.delete(customer).where(customer.level.lt(3)).execute();

where 調用是可選的,execute 調用執行刪除並返回已刪除實體的數量。

JPA 中的 DML 子句不考慮 JPA 級級聯規則,也不提供細粒度的二級緩存交互。

2.1.12. Update 子句

Querydsl JPA 中的更新子句遵循簡單的更新集/位置執行形式。這里有些例子:

QCustomer customer = QCustomer.customer;
// 將名為 Bob 的客戶重命名為 Bobby
queryFactory.update(customer).where(customer.name.eq("Bob"))
    .set(customer.name, "Bobby")
    .execute();

集合調用以 SQL 更新樣式定義屬性更新,執行調用執行更新並返回更新實體的數量。

JPA 中的 DML 子句不考慮 JPA 級級聯規則,也不提供細粒度的二級緩存交互。

2.1.13. 子查詢

要創建子查詢,您可以使用“JPAExpressions”的靜態工廠方法並通過 from、where 等定義查詢參數。

QDepartment department = QDepartment.department;
QDepartment d = new QDepartment("d");
queryFactory.selectFrom(department)
    .where(department.size.eq(
        JPAExpressions.select(d.size.max()).from(d)))
     .fetch();

另一個例子

QEmployee employee = QEmployee.employee;
QEmployee e = new QEmployee("e");
queryFactory.selectFrom(employee)
    .where(employee.weeklyhours.gt(
        JPAExpressions.select(e.weeklyhours.avg())
            .from(employee.department.employees, e)
            .where(e.manager.eq(employee.manager))))
    .fetch();

2.1.14. 公開原始查詢

如果您需要在執行查詢之前調整原始查詢,您可以像這樣公開它:

Query jpaQuery = queryFactory.selectFrom(employee).createQuery();
// ...
List results = jpaQuery.getResultList();

2.1.15。在 JPA 查詢中使用原生 SQL

Querydsl 通過 JPASQLQuery 類支持 JPA 中的原生 SQL。

要使用它,您必須為您的 SQL 模式生成 Querydsl 查詢類型。例如,這可以通過以下 Maven 配置來完成:

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>export</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <jdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</jdbcDriver>
          <jdbcUrl>jdbc:derby:target/demoDB;create=true</jdbcUrl>
          <packageName>com.mycompany.mydomain</packageName>
          <targetFolder>${project.basedir}/target/generated-sources/java</targetFolder>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>${derby.version}</version>
          </dependency>
        </dependencies>
      </plugin>
      ...
    </plugins>
  </build>
</project>

當查詢類型成功生成到您選擇的位置后,您可以在查詢中使用它們。

單列查詢:

// 序列化模板
SQLTemplates templates = new DerbyTemplates();
// 查詢類型(S* 代表 SQL,Q* 代表域類型)
SAnimal cat = new SAnimal("cat");
SAnimal mate = new SAnimal("mate");
QCat catEntity = QCat.cat;

JPASQLQuery<?> query = new JPASQLQuery<Void>(entityManager, templates);
List<String> names = query.select(cat.name).from(cat).fetch();

如果您在查詢中混合使用實體(例如 QCat)和表(例如 SAnimal)引用,您需要確保它們使用相同的變量名。SAnimal.animal 的變量名為“animal”,因此使用了一個新實例 (new SAnimal("cat"))。

另一種模式可能是

QCat catEntity = QCat.cat;
SAnimal cat = new SAnimal(catEntity.getMetadata().getName());

查詢多列:

query = new JPASQLQuery<Void>(entityManager, templates);
List<Tuple> rows = query.select(cat.id, cat.name).from(cat).fetch();

查詢所有列:

List<Tuple> rows = query.select(cat.all()).from(cat).fetch();

在 SQL 中查詢,但作為實體項目:

query = new JPASQLQuery<Void>(entityManager, templates);
List<Cat> cats = query.select(catEntity).from(cat).orderBy(cat.name.asc()).fetch();

使用連接查詢:

query = new JPASQLQuery<Void>(entityManager, templates);
cats = query.select(catEntity).from(cat)
    .innerJoin(mate).on(cat.mateId.eq(mate.id))
    .where(cat.dtype.eq("Cat"), mate.dtype.eq("Cat"))
    .fetch();

查詢並投影到 DTO:

query = new JPASQLQuery<Void>(entityManager, templates);
List<CatDTO> catDTOs = query.select(Projections.constructor(CatDTO.class, cat.id, cat.name))
    .from(cat)
    .orderBy(cat.name.asc())
    .fetch();

如果您使用的是 Hibernate API 而不是 JPA API,那么請改用“HibernateSQLQuery”。

2.2. 查詢 JDO

Querydsl 定義了一種通用的靜態類型語法,用於在持久的域模型數據之上進行查詢。JDO 和 JPA 是 Querydsl 的主要集成技術。本指南描述了如何將 Querydsl 與 JDO 結合使用。

2.2.1. Maven 集成

將以下依賴項添加到您的 Maven 項目中:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jdo</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

現在,配置生成 Querydsl 使用的查詢類型的 Maven APT 插件:

<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.jdo.JDOAnnotationProcessor</processor>
            </configuration>
          </execution>
        </executions>
      </plugin>
    ...
    </plugins>
  </build>
</project>

JDOAnnotationProcessor 查找使用 javax.jdo.annotations.PersistenceCapable 批注的域類型,並為它們生成查詢類型。

運行全新安裝,您將在 target/generated-sources/java 中生成查詢類型。

如果您使用 Eclipse,請運行 mvn eclipse:eclipse 以更新您的 Eclipse 項目以包含 target/generated-sources/java 作為源文件夾。

現在您可以構造 JDO 查詢實例和查詢域模型的實例。

2.2.2. Ant 集成

將來自 full-deps 包的 jar 文件放在類路徑中,並使用以下任務生成 Querydsl 代碼:

    <!-- 基於 APT 的代碼生成 -->
    <javac srcdir="${src}" classpathref="cp">
      <compilerarg value="-proc:only"/>
      <compilerarg value="-processor"/>
      <compilerarg value="com.querydsl.apt.jdo.JDOAnnotationProcessor"/>
      <compilerarg value="-s"/>
      <compilerarg value="${generated}"/>
    </javac>

    <!-- 編譯 -->
    <javac classpathref="cp" destdir="${build}">
      <src path="${src}"/>
      <src path="${generated}"/>
    </javac>

src 替換為您的主源文件夾,將 generated 替換為生成的源文件夾,將 build 替換為目標文件夾。

2.2.3. 使用查詢類型

要使用 Querydsl 創建查詢,您需要實例化變量和 Query 實現。我們將從變量開始。

假設您的項目具有以下域類型:

@PersistenceCapable
public class Customer {
  private String firstName;
  private String lastName;

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setFirstName(String fn) {
    firstName = fn;
  }

  public void setLastName(String ln) {
    lastName = ln;
  }
}

Querydsl 將生成一個簡單名稱為 QCustomer 的查詢類型到與 Customer 相同的包中。QCustomer 可以用作 Querydsl 中的靜態類型變量,作為 Customer 類型的代表。

QCustomer 有一個可以作為靜態字段訪問的默認實例變量:

QCustomer customer = QCustomer.customer;

或者,您可以像這樣定義自己的客戶變量:

QCustomer customer = new QCustomer("myCustomer");

QCustomer 將原始類型 Customer 的所有屬性反映為公共字段。可以像這樣訪問 firstName 字段

customer.firstName;

2.2.4. 使用 JDO 查詢

對於 JDO 模塊,JDOQuery 是主要的 Query 實現。它是這樣實例化的:

PersistenceManager pm = ...;
JDOQuery<?> query = new JDOQuery<Void>(pm);

對於本章的示例,查詢是通過 JDOQueryFactory 實例創建的。JDOQueryFactory 應該是獲取 JDOQuery 實例的首選選項。

要檢索名字為 Bob 的客戶,您將構建如下查詢:

QCustomer customer = QCustomer.customer;
Customer bob = queryFactory.selectFrom(customer)
                   .where(customer.firstName.eq("Bob"))
                   .fetchOne();

selectFrom 調用定義了查詢源和投影,where 部分定義了過濾器,而 fetchOne 告訴 Querydsl 返回單個元素。很容易,對吧?

或者你也可以這樣表達

QCustomer customer = QCustomer.customer;
Customer bob = queryFactory.select(customer).from(customer)
                   .where(customer.firstName.eq("Bob"))
                   .fetchOne();

要創建具有多個源的查詢,您只需使用 JDOQuery 類,如下所示:

QCustomer customer = QCustomer.customer;
QCompany company = QCompany.company;
query.from(customer, company);

並使用多個過濾器像這樣使用它

queryFactory.selectFrom(customer)
    .where(customer.firstName.eq("Bob"), customer.lastName.eq("Wilson"));

或者像這樣

queryFactory.selectFrom(customer)
    .where(customer.firstName.eq("Bob").and(customer.lastName.eq("Wilson")));

如果您想通過“或”組合過濾器,請使用以下模式

queryFactory.selectFrom(customer)
    .where(customer.firstName.eq("Bob").or(customer.lastName.eq("Wilson")));

2.2.5. 一般用法

像這樣使用 JDOQuery 類的級聯方法

select: 設置查詢的投影。(如果通過查詢工廠創建則不需要)

from: 在此處添加查詢源,第一個參數成為主要來源,其他參數視為變量。

where: 添加查詢過濾器,以可變參數形式通過逗號分隔或通過 and 運算符級聯。

groupBy: 以可變參數形式添加分組參數。

have: 添加具有“group by”分組的過濾器作為謂詞表達式的可變參數數組。

orderBy: 將結果的順序添加為順序表達式的可變參數數組。對數字、字符串和其他可比較的表達式使用 asc() 和 desc() 來訪問 OrderSpecifier 實例。

limit,offset,restrict: 設置結果的分頁。最大結果的限制,跳過行的偏移量和一次調用中定義兩者的限制。

2.2.6. Ordering

聲明排序的語法是

QCustomer customer = QCustomer.customer;
queryFactory.selectFrom(customer)
    .orderBy(customer.lastName.asc(), customer.firstName.desc())
    .fetch();

2.2.7. Grouping

分組可以按以下形式進行

queryFactory.select(customer.lastName).from(customer)
    .groupBy(customer.lastName)
    .fetch();

2.2.8. Delete 子句

Querydsl JDO 中的 Delete 子句遵循簡單的 delete-where-execute 形式。這里有些例子:

QCustomer 客戶 = QCustomer.customer;
//刪除所有客戶
queryFactory.delete(customer).execute();
// 刪除所有等級小於 3 的客戶
queryFactory.delete(customer).where(customer.level.lt(3)).execute();

JDODeleteClause 構造函數的第二個參數是要刪除的實體。where 調用是可選的,execute 調用執行刪除並返回已刪除實體的數量。

2.2.9. 子查詢

要創建子查詢,您可以使用“JDOExpressions”的工廠方法之一,並通過 from、where 等添加查詢參數。

QDepartment department = QDepartment.department;
QDepartment d = new QDepartment("d");
queryFactory.selectFrom(department)
    .where(department.size.eq(JDOExpressions.select(d.size.max()).from(d))
    .fetch();

表示以下原生 JDO 查詢

SELECT this FROM com.querydsl.jdo.models.company.Department
WHERE this.size ==
(SELECT max(d.size) FROM com.querydsl.jdo.models.company.Department d)

另一個例子

QEmployee employee = QEmployee.employee;
QEmployee e = new QEmployee("e");
queryFactory.selectFrom(employee)
    .where(employee.weeklyhours.gt(
        JDOExpressions.select(e.weeklyhours.avg())
                      .from(employee.department.employees, e)
                      .where(e.manager.eq(employee.manager)))
    .fetch();

它表示以下原生 JDO 查詢

SELECT this FROM com.querydsl.jdo.models.company.Employee
WHERE this.weeklyhours >
(SELECT avg(e.weeklyhours) FROM this.department.employees e WHERE e.manager == this.manager)

2.2.10. 使用原生 SQL

Querydsl 通過 JDOSQLQuery 類支持 JDO 中的 Native SQL。

要使用它,您必須為您的 SQL 模式生成 Querydsl 查詢類型。例如,這可以通過以下 Maven 配置來完成:

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>export</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <jdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</jdbcDriver>
          <jdbcUrl>jdbc:derby:target/demoDB;create=true</jdbcUrl>
          <packageName>com.mycompany.mydomain</packageName>
          <targetFolder>${project.basedir}/target/generated-sources/java</targetFolder>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>${derby.version}</version>
          </dependency>
        </dependencies>
      </plugin>
      ...
    </plugins>
  </build>
</project>

當查詢類型成功生成到您選擇的位置后,您可以在查詢中使用它們。

單列查詢:

// 序列化模板
SQLTemplates templates = new DerbyTemplates();
// 查詢類型(S* 代表 SQL,Q* 代表域類型)
SAnimal cat = new SAnimal("cat");
SAnimal mate = new SAnimal("mate");

JDOSQLQuery<?> query = new JDOSQLQuery<Void>(pm, templates);
List<String> names = query.select(cat.name).from(cat).fetch();

查詢多列:

query = new JDOSQLQuery<Void>(pm, templates);
List<Tuple> rows = query.select(cat.id, cat.name).from(cat).fetch();

查詢所有列:

List<Tuple> rows = query.select(cat.all()).from(cat).fetch();

使用聯結查詢:

query = new JDOSQLQuery<Void>(pm, templates);
cats = query.select(catEntity).from(cat)
    .innerJoin(mate).on(cat.mateId.eq(mate.id))
    .where(cat.dtype.eq("Cat"), mate.dtype.eq("Cat"))
    .fetch();

查詢並投影到 DTO:

query = new JDOSQLQuery<Void>(pm, templates);
List<CatDTO> catDTOs = query.select(Projections.constructor(CatDTO.class, cat.id, cat.name))
    .from(cat)
    .orderBy(cat.name.asc())
    .fetch();

2.3. 查詢 SQL

本章介紹 SQL 模塊的查詢類型生成和查詢功能。

2.3.1. Maven 集成

將以下依賴項添加到您的 Maven 項目中:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-sql</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-sql-codegen</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

如果通過 Maven 生成代碼,則可以跳過 querydsl-sql-codegen 依賴項。

2.3.2。通過 Maven 生成代碼

此功能應主要通過 Maven 插件使用。這是一個例子:

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>export</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <jdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</jdbcDriver>
          <jdbcUrl>jdbc:derby:target/demoDB;create=true</jdbcUrl>
          <packageName>com.myproject.domain</packageName>
          <targetFolder>${project.basedir}/target/generated-sources/java</targetFolder>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>${derby.version}</version>
          </dependency>
        </dependencies>
      </plugin>
      ...
    </plugins>
  </build>
</project>

使用目標 test-export 將目標文件夾視為用於測試代碼的測試源文件夾。

表 2.1. 參數

姓名 說明
jdbcDriver JDBC 驅動的類名
jdbcUrl JDBC 網址
jdbcUser JDBC 用戶
jdbcPassword JDBC 密碼
namePrefix 生成的查詢類的名稱前綴(默認值:Q)
nameSuffix 生成的查詢類的名稱后綴(默認值:)
beanPrefix 生成的 bean 類的名稱前綴
beanSuffix 生成的 bean 類的名稱后綴
packageName 應生成源文件的包名
beanPackageName 應生成 bean 文件的包名,(默認值:packageName)
beanInterfaces 要添加到 bean 類的接口類名​​數組(默認值:空)
beanAddToString 設置為 true 以創建默認 toString() 實現(默認值:false)
beanAddFullConstructor 設置為 true 以創建除公共空之外的完整構造函數(默認值:false)
beanPrintSupertype 設置為 true 以打印超類型(默認值:false)
schemaPattern LIKE 模式形式的模式名稱模式;必須與存儲在數據庫中的模式名稱匹配,多個可以用逗號分隔(默認值:null)
tableNamePattern LIKE 模式形式的表名模式;必須與存儲在數據庫中的表名匹配,多個可以用逗號分隔(默認:null)
targetFolder 應生成源的目標文件夾
beansTargetFolder 應生成 bean 源的目標文件夾,默認為與 targetFolder
namingStrategyClass NamingStrategy 類的類名(默認值:DefaultNamingStrategy)
beanSerializerClass BeanSerializer 類的類名(默認:BeanSerializer)
serializerClass Serializer 類的類名(默認:MetaDataSerializer)
exportBeans 設置為 true 也可以生成 bean,請參閱第 2.14.13 節(默認值:false)
innerClassesForKeys 設置為 true 以生成鍵的內部類(默認值:false)
validationAnnotations 設置為 true 以啟用驗證注釋的序列化(默認值:false)
columnAnnotations 導出列注釋(默認值:false)
createScalaSources 是否導出 Scala 源而不是 Java 源,(默認值:false)
schemaToPackage 將架構名稱附加到包(默認值:false)
lowerCase 名稱的小寫轉換(默認值:false)
exportTables 導出表(默認:true)
exportViews 導出視圖(默認值:true)
exportPrimaryKeys 導出主鍵(默認:true)
tableTypesToExport 要導出的表類型的逗號分隔列表(允許的值取決於 JDBC 驅動程序)。允許導出任意類型的集合,例如:“TABLE, MATERIALIZED VIEW”。如果設置了此參數,將忽略 exportTables 和 exportViews 參數。(默認:無)
exportForeignKeys 導出外鍵(默認:true)
exportDirectForeignKeys 導出直接外鍵(默認值:true)
exportInverseForeignKeys 導出反向外鍵(默認值:true)
customTypes 自定義用戶類型(默認:無)
typeMappings table.column 到 Java 類型的映射(默認值:無)
numericMappings 大小/數字到 Java 類型的映射(默認值:無)
imports 添加到生成的查詢類的 java 導入數組: com.bar 用於包(不帶 .* 符號),com.bar.Foo 用於類(默認值:空)

自定義類型可用於注冊其他類型實現:

<customTypes>
  <customType>com.querydsl.sql.types.InputStreamType</customType>
</customTypes>

類型映射可用於注冊 table.column 特定的 java 類型:

<typeMappings>
  <typeMapping>
    <table>IMAGE</table>
    <column>CONTENTS</column>
    <type>java.io.InputStream</type>
  </typeMapping>
</typeMappings>

數字映射的默認值是

表 2.2。數字映射

總位數 小數位 類型
> 18 0 BigInteger
> 9 0 Long
> 4 0 Integer
> 2 0 Short
> 0 0 Byte
> 0 > 0 BigDecimal

它們可以針對特定的總/小數位數組合進行定制,如下所示:

<numericMappings>
  <numericMapping>
    <total>1</total>
    <decimal>0</decimal>
    <javaType>java.lang.Byte</javaType>
  </numericMapping>
</numericMappings>

導入可用於添加跨模式外鍵支持。

模式、表和列也可以使用插件重命名。這里有些例子:

重命名架構:

<renameMappings>
  <renameMapping>
    <fromSchema>PROD</fromSchema>
    <toSchema>TEST</toSchema>
  </renameMapping>
</renameMappings>

重命名表:

<renameMappings>
  <renameMapping>
    <fromSchema>PROD</fromSchema>
    <fromTable>CUSTOMER</fromTable>
    <toTable>CSTMR</toTable>
  </renameMapping>
</renameMappings>

重命名列:

<renameMappings>
  <renameMapping>
    <fromSchema>PROD</fromSchema>
    <fromTable>CUSTOMER</fromTable>
    <fromColumn>ID</fromColumn>
    <toColumn>IDX</toTable>
  </renameMapping>
</renameMappings>

注意:重命名表和列時可以省略 fromSchema。

與基於 APT 的代碼生成相比,某些功能不可用,例如 QueryDelegate 注釋處理。

2.3.3. 通過 ANT 生成代碼

querydsl-sql 模塊的 ANT 任務 com.querydsl.sql.codegen.ant.AntMetaDataExporter 提供與 ANT 任務相同的功能。該任務的配置參數與 Maven 插件相同,但復合類型除外。

復合類型在沒有包裝元素的情況下使用,如本例所示。

<project name="testproject" default="codegen" basedir=".">

  <taskdef name="codegen" classname="com.querydsl.sql.codegen.ant.AntMetaDataExporter"/>

  <target name="codegen">
    <codegen
      jdbcDriver="org.h2.Driver"
      jdbcUser="sa"
      jdbcUrl="jdbc:h2:/dbs/db1"
      packageName="test"
      targetFolder="target/generated-sources/java">
      <renameMapping fromSchema="PUBLIC" toSchema="PUB"/>
    </codegen>
  </target>
</project>

2.3.4. 創建查詢類型

要開始將架構導出為 Querydsl 查詢類型,如下所示:

java.sql.Connection conn = ...;
MetaDataExporter exporter = new MetaDataExporter();
exporter.setPackageName("com.myproject.mydomain");
exporter.setTargetFolder(new File("target/generated-sources/java"));
exporter.export(conn.getMetaData());

這聲明數據庫模式將被鏡像到 target/generated-sources/java 文件夾中的 com.myproject.domain 包中。

生成的類型將表名轉換為混合大小寫作為類名,並將類似的混合大小寫轉換應用於可用作查詢類型中的屬性路徑的列。

除了這個主鍵和外鍵約束之外,還提供了可用於緊湊連接聲明的字段。

2.3.5. 配置

配置是通過 com.querydsl.sql.Configuration 類完成的,該類將 Querydsl SQL 方言作為參數。對於 H2,您將像這樣創建它

SQLTemplates templates = new H2Templates();
Configuration configuration = new Configuration(templates);

Querydsl 使用 SQL 方言來自定義不同關系數據庫所需的 SQL 序列化。可用的方言是:

  • CUBRIDTemplates(使用 CUBRID 8.4 測試)
  • DB2Templates(使用 DB2 10.1.2 測試)
  • DerbyTemplates(使用 Derby 10.8.2.2 測試)
  • FirebirdTemplates(用 Firebird 2.5 測試)
  • HSQLDBTemplates(使用 HSQLDB 2.2.4 測試)
  • H2Templates(使用 H2 1.3.164 測試)
  • MySQLTemplates(用 MySQL 5.5 測試)
  • OracleTemplates(使用 Oracle 10 和 11 進行測試)
  • PostgreSQLTemplates(用 PostgreSQL 9.1 測試)
  • SQLiteTemplates(使用 xerial JDBC 3.7.2 測試)
  • SQLServerTemplates(使用 SQL Server 測試)
  • SQLServer2005模板(用於 SQL Server 2005)
  • SQLServer2008 模板(適用於 SQL Server 2008)
  • SQLServer2012模板(適用於 SQL Server 2012 及更高版本)
  • TeradataTemplates(使用 Teradata 14 測試)

對於自定義 SQLTemplates 實例,您可以像這樣使用構建器模式

  H2Templates.builder()
     .printSchema() // 在輸出中包含模式
     .quote()       // 引用名稱
     .newLineToSingleSpace() // 在輸出中用單個空格替換新行
     .escape(ch)    // 設置轉義字符
     .build();      // 獲取自定義的 SQLTemplates 實例

Configuration 類的方法可用於通過 setUseLiterals(true) 啟用文字的直接序列化,覆蓋模式和表並注冊自定義類型。有關完整的詳細信息,請查看 Configuration 的 javadocs。

2.3.6. 查詢

對於以下示例,我們將使用 SQLQueryFactory 類來創建查詢。與基於構造函數的查詢創建相比,使用它可以生成更簡潔的代碼。

SQLQueryFactory queryFactory = new SQLQueryFactory(configuration, dataSource);

使用 Querydsl SQL 查詢就這么簡單:

QCustomer customer = new QCustomer("c");

List<String> lastNames = queryFactory.select(customer.lastName).from(customer)
    .where(customer.firstName.eq("Bob"))
    .fetch();

假設相關表名為customer,列first_namelast_name,則轉化為如下sql查詢:

SELECT c.last_name
FROM customer c
WHERE c.first_name = 'Bob'

2.3.7. 一般用法

像這樣使用 SQLQuery 類的級聯方法

select: 設置查詢的投影。(如果通過查詢工廠創建則不需要)

from: 在此處添加查詢源。

innerJoin, join, leftJoin, rightJoin, fullJoin, on: 使用這些結構添加連接元素。對於連接方法,第一個參數是連接源,第二個是目標(別名)。

where: 添加查詢過濾器,以可變參數形式通過逗號分隔或通過 and 運算符級聯。

groupBy: 以可變參數形式添加分組參數。

have: 添加具有“group by”分組的過濾器作為謂詞表達式的 varags 數組。

orderBy: 將結果的順序添加為順序表達式的可變參數數組。對數字、字符串和其他可比較的表達式使用 asc() 和 desc() 來訪問 OrderSpecifier 實例。

limit,offset,restrict: 設置結果的分頁。最大結果的限制,跳過行的偏移量和一次調用中定義兩者的限制。

2.3.8. 聯結

聯結是使用以下語法構造的:

QCustomer customer = QCustomer.customer;
QCompany company = QCompany.company;
queryFactory.select(customer.firstName, customer.lastName, company.name)
    .from(customer)
    .innerJoin(customer.company, company)
    .fetch();

對於左聯結:

queryFactory.select(customer.firstName, customer.lastName, company.name)
    .from(customer)
    .leftJoin(customer.company, company)
    .fetch();

或者,也可以寫出聯結條件:

queryFactory.select(customer.firstName, customer.lastName, company.name)
    .from(customer)
    .leftJoin(company).on(customer.company.eq(company))
    .fetch();

2.3.9. Ordering

聲明排序的語法是

queryFactory.select(customer.firstName, customer.lastName)
    .from(customer)
    .orderBy(customer.lastName.asc(), customer.firstName.asc())
    .fetch();

相當於下面的原生 SQL

SELECT c.first_name, c.last_name
FROM customer c
ORDER BY c.last_name ASC, c.first_name ASC

2.3.10. Grouping

分組可以按以下形式進行

queryFactory.select(customer.lastName)
    .from(customer)
    .groupBy(customer.lastName)
    .fetch();

相當於下面的原生 SQL

SELECT c.last_name
FROM customer c
GROUP BY c.last_name

2.3.11. 使用子查詢

要創建子查詢,您可以使用“SQLExpressions”的工廠方法之一,並通過 from、where 等添加查詢參數。

QCustomer customer = QCustomer.customer;
QCustomer customer2 = new QCustomer("customer2");
queryFactory.select(customer.all())
    .from(customer)
    .where(customer.status.eq(
        SQLExpressions.select(customer2.status.max()).from(customer2)))
    .fetch()

另一個例子

QStatus status = QStatus.status;
queryFactory.select(customer.all())
    .from(customer)
    .where(customer.status.in(
        SQLExpressions.select(status.id).from(status).where(status.level.lt(3))))
    .fetch();

2.3.12. 選擇文字

要選擇文字,您需要為它們創建常量實例,如下所示:

queryFactory.select(Expressions.constant(1),
                    Expressions.constant("abc"));

com.querydsl.core.types.dsl.Expressions 類還為投影、操作和模板創建提供了其他有用的靜態方法。

2.3.13. 查詢擴展支持

可以通過繼承 AbstractSQLQuery 並添加標記方法來創建支持引擎特定語法的自定義查詢擴展,例如給定的 MySQLQuery 示例:

public class MySQLQuery<T> extends AbstractSQLQuery<T, MySQLQuery<T>> {

    public MySQLQuery(Connection conn) {
        this(conn, new MySQLTemplates(), new DefaultQueryMetadata());
    }

    public MySQLQuery(Connection conn, SQLTemplates templates) {
        this(conn, templates, new DefaultQueryMetadata());
    }

    protected MySQLQuery(Connection conn, SQLTemplates templates, QueryMetadata metadata) {
        super(conn, new Configuration(templates), metadata);
    }

    public MySQLQuery bigResult() {
        return addFlag(Position.AFTER_SELECT, "SQL_BIG_RESULT ");
    }

    public MySQLQuery bufferResult() {
        return addFlag(Position.AFTER_SELECT, "SQL_BUFFER_RESULT ");
    }


    // ...
}

這些標志是自定義 SQL 片段,可以插入到序列化的特定點。支持的位置是 com.querydsl.core.QueryFlag.Position 枚舉類的枚舉。

2.3.14. 窗口函數

Querydsl 通過 SQLExpressions 類中的方法支持窗口函數。

使用示例:

queryFactory.select(SQLExpressions.rowNumber()
        .over()
        .partitionBy(employee.name)
        .orderBy(employee.id))
     .from(employee)

2.3.15. 公用表表達式

Querydsl SQL 通過兩種語法變體支持公用表表達式

QEmployee employee = QEmployee.employee;
queryFactory.with(employee, SQLExpressions.select(employee.all)
                                          .from(employee)
                                          .where(employee.name.startsWith("A")))
            .from(...)

並使用列列表

QEmployee employee = QEmployee.employee;
queryFactory.with(employee, employee.id, employee.name)
            .as(SQLExpressions.select(employee.id, employee.name)
                              .from(employee)
                              .where(employee.name.startsWith("A")))
            .from(...)

如果公用表表達式的列是現有表或視圖的子集,建議使用生成的路徑類型,例如在這種情況下為 QEmployee,但如果列不適合任何現有表,則可以使用 PathBuilder反而。

以下是此類情況的示例

QEmployee employee = QEmployee.employee;
QDepartment department = QDepartment.department;
PathBuilder<Tuple> emp = new PathBuilder<Tuple>(Tuple.class, "emp");
queryFactory.with(emp, SQLExpressions.select(employee.id, employee.name, employee.departmentId,
                                          department.name.as("departmentName"))
                                      .from(employee)
                                      .innerJoin(department).on(employee.departmentId.eq(department.id))))
            .from(...)

2.3.16. 其他 SQL 表達式

SQLExpressions 類中還提供了其他 SQL 表達式作為靜態方法。

2.3.17. 使用數據操作命令

2.3.17.1. 插入

帶列

QSurvey survey = QSurvey.survey;

queryFactory.insert(survey)
    .columns(survey.id, survey.name)
    .values(3, "Hello").execute();

無列

queryFactory.insert(survey)
    .values(4, "Hello").execute();

帶子查詢

queryFactory.insert(survey)
    .columns(survey.id, survey.name)
    .select(SQLExpressions.select(survey2.id.add(1), survey2.name).from(survey2))
    .execute();

有子查詢,沒有列

queryFactory.insert(survey)
    .select(SQLExpressions.select(survey2.id.add(10), survey2.name).from(survey2))
    .execute();

作為列/值用法的替代方法,Querydsl 還提供了一個 set 方法,可以像這樣使用

QSurvey survey = QSurvey.survey;

queryFactory.insert(survey)
    .set(survey.id, 3)
    .set(survey.name, "Hello").execute();

這相當於第一個例子。set 方法的使用總是在內部擴展到列和值。

請注意

columns(...).select(...)

映射要插入的給定查詢的結果集,而

要獲取創建的鍵而不是修改的行數,請使用其中一種 executeWithKey/s 方法。

set(...)

映射單列和空值用於空子查詢結果。

要根據 bean 的內容填充子句實例,您可以使用

queryFactory.insert(survey)
    .populate(surveyBean).execute();

這將排除空綁定,如果您還需要空綁定使用

queryFactory.insert(survey)
    .populate(surveyBean, DefaultMapper.WITH_NULL_BINDINGS).execute();
2.3.17.2. Update

使用 where

QSurvey survey = QSurvey.survey;

queryFactory.update(survey)
    .where(survey.name.eq("XXX"))
    .set(survey.name, "S")
    .execute();

沒有 where

queryFactory.update(survey)
    .set(survey.name, "S")
    .execute();

使用 bean 種群

queryFactory.update(survey)
    .populate(surveyBean)
    .execute();
2.3.17.3. Delete

使用 where

QSurvey survey = QSurvey.survey;

queryFactory.delete(survey)
    .where(survey.name.eq("XXX"))
    .execute();

沒有 where

queryFactory.delete(survey)
    .execute()

2.3.18. DML 子句中的批處理支持

Querydsl SQL 支持通過 DML API 使用 JDBC 批量更新。如果您有連續的具有相似結構的 DML 調用,則可以通過 addBatch() 用法將調用捆綁到一個 DMLClause 中。請參閱示例如何處理 UPDATE、DELETE 和 INSERT。

Update:

QSurvey 調查 = QSurvey.survey;

queryFactory.insert(survey).values(2, "A").execute();
queryFactory.insert(survey).values(3, "B").execute();

SQLUpdateClause update = queryFactory.update(survey);
update.set(survey.name, "AA").where(survey.name.eq("A")).addBatch();
update.set(survey.name, "BB").where(survey.name.eq("B")).addBatch();

Delete:

queryFactory.insert(survey).values(2, "A").execute();
queryFactory.insert(survey).values(3, "B").execute();

SQLDeleteClause delete = queryFactory.delete(survey);
delete.where(survey.name.eq("A")).addBatch();
delete.where(survey.name.eq("B")).addBatch();
assertEquals(2, delete.execute());

Insert:

SQLInsertClause insert = queryFactory.insert(survey);
insert.set(survey.id, 5).set(survey.name, "5").addBatch();
insert.set(survey.id, 6).set(survey.name, "6").addBatch();
assertEquals(2, insert.execute());

2.3.19. Bean 類生成

要為模式的表創建 JavaBean DTO 類型,請使用 MetaDataExporter,如下所示:

java.sql.Connection conn = ...;
MetaDataExporter exporter = new MetaDataExporter();
exporter.setPackageName("com.myproject.mydomain");
exporter.setTargetFolder(new File("src/main/java"));
exporter.setBeanSerializer(new BeanSerializer());
exporter.export(conn.getMetaData());

現在,您可以將 bean 類型用作 DML 子句中的 populate 方法的參數,並且可以直接投影到查詢中的 bean 類型。這是一個 JUnit 形式的簡單示例:

QEmployee e = new QEmployee("e");

// 插入
Employee employee = new Employee();
employee.setFirstname("John");
Integer id = queryFactory.insert(e).populate(employee).executeWithKey(e.id);
employee.setId(id);

// 更新
employee.setLastname("Smith");
assertEquals(1l, queryFactory.update(e).populate(employee).where(e.id.eq(employee.getId())).execute());

// 詢問
Employee smith = queryFactory.selectFrom(e).where(e.lastname.eq("Smith")).fetchOne();
assertEquals("John", smith.getFirstname());

// 刪除
assertEquals(1l, queryFactory.delete(e).where(e.id.eq(employee.getId())).execute());

2.3.20. 提取 SQL 查詢和綁定

SQL 查詢和綁定可以通過 getSQL 方法提取:

SQLBindings bindings = query.getSQL();
System.out.println(bindings.getSQL());

如果您還需要 SQL 字符串中的所有文字,您可以通過 setUseLiterals(true) 在查詢或配置級別啟用文字序列化。

2.3.21. 自定義類型

Querydsl SQL 提供了為 ResultSet/Statement 交互聲明自定義類型映射的可能性。自定義類型映射可以在 com.querydsl.sql.Configuration 實例中聲明,這些實例作為構造函數參數提供給實際查詢:

Configuration configuration = new Configuration(new H2Templates());
// 覆蓋 Types.DATE 的映射
configuration.register(new UtilDateType());

對於表格列

Configuration configuration = new Configuration(new H2Templates());
// 為 person 表中的性別列聲明一個映射
configuration.register("person", "gender", new EnumByNameType<Gender>(Gender.class));

要自定義數字映射,您可以像這樣使用 registerNumeric 方法

configuration.registerNumeric(5,2,Float.class);

這會將 Float 類型映射到 NUMERIC(5,2) 類型。

2.3.22. 偵聽查詢和子句

SQLListener 是一個偵聽器接口,可用於偵聽查詢和 DML 子句。SQLListener 實例可以通過 addListener 方法在配置和查詢/子句級別上注冊。

偵聽器的用例是數據同步、日志記錄、緩存和驗證。

2.3.23. Spring 集成

Querydsl SQL 通過 querydsl-sql-spring 模塊與 Spring 集成:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-sql-spring</artifactId>
  <version>${querydsl.version}</version>
</dependency>

它提供了 Spring 異常翻譯和 Spring 連接提供程序,用於將 Querydsl SQL 與 Spring 事務管理器一起使用。下面是一個配置示例:

package com.querydsl.example.config;

import com.querydsl.sql.H2Templates;
import com.querydsl.sql.SQLQueryFactory;
import com.querydsl.sql.SQLTemplates;
import com.querydsl.sql.spring.SpringConnectionProvider;
import com.querydsl.sql.spring.SpringExceptionTranslator;
import com.querydsl.sql.types.DateTimeType;
import com.querydsl.sql.types.LocalDateType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.sql.DataSource;
import java.sql.Connection;

@Configuration
public class JdbcConfiguration {

    @Bean
    public DataSource dataSource() {
        // implementation omitted
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public com.querydsl.sql.Configuration querydslConfiguration() {
        SQLTemplates templates = H2Templates.builder().build(); //change to your Templates
        com.querydsl.sql.Configuration configuration = new com.querydsl.sql.Configuration(templates);
        configuration.setExceptionTranslator(new SpringExceptionTranslator());
        return configuration;
    }

    @Bean
    public SQLQueryFactory queryFactory() {
        Provider<Connection> provider = new SpringConnectionProvider(dataSource());
        return new SQLQueryFactory(querydslConfiguration(), provider);
    }

}

2.4. Querydsl 空間

可通過 Querydsl Spatial 模塊獲得對空間查詢的支持,該模塊是 SQL 模塊的擴展模塊。Spatial 模塊在查詢和對象綁定中支持簡單特征訪問的對象模型。

geolatte 項目用於對象模型。

空間

2.4.1. Maven 集成

將以下依賴項添加到您的 Maven 項目中:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-sql-spatial</artifactId>
  <version>${querydsl.version}</version>
</dependency>

此外,以下數據庫特定的額外依賴項:

<!-- 用於 PostgreSQL 的 -->
<dependency>
  <groupId>org.postgis</groupId>
  <artifactId>postgis-jdbc</artifactId>
  <version>1.3.3</version>
  <scope>provided</scope>
</dependency>

<!-- 供 Oracle 使用 -->
<dependency>
  <groupId>oracle</groupId>
  <artifactId>sdoapi</artifactId>
  <version>11.2.0</version>
  <scope>provided</scope>
</dependency>

2.4.2. 通過 Maven 生成代碼

Querydsl SQL 的代碼生成可以設置為檢測數據庫模式中空間類型的使用,並在這些情況下通過空間屬性使用 geolatte 類型:

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        ...
        <configuration>
          ...
          <spatial>true</spatial>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>
</project>

2.4.3. 運行時配置

空間模塊的運行時配置方面是使用啟用空間的實例而不是普通的 SQLTemplates 實例。下面是啟用空間的 SQLTemplates 類的列表。

  • GeoDBTemplates(用於 H2)
  • MySQL空間模板
  • OracleSpatialTemplates(阿爾法階段)
  • PostGIS模板
  • SQLServer2008空間模板
  • TeradataSpatialTemplates

2.4.4. 查詢

通過為空間類型設置代碼生成和運行時配置,我們現在可以嘗試使用它進行查詢。

2.4.4.1. 按距離過濾
Geometry point = Wkt.fromWkt("Point(2 2)");
query.where(table.geo.distance(point).lt(5.0));

除了幾何之間的直線距離之外,還通過 distanceSphere 和 distanceSpheroid 提供了球面和球面距離。

2.4.4.2. 包含
Geometry point = Wkt.fromWkt("Point(2 2)");
query.where(table.geo.contains(point));
2.4.4.3. 相交
Geometry geo = query.select(table.geo1.intersection(table.geo2)).fetchOne();
2.4.4.4. 訪問 SPATIAL_REF_SYS 表

通過 QSpatialRefSys 和 SpatialRefSys 類提供對 SPATIAL_REF_SYS 標准表的統一訪問。SPATIAL_REF_SYS 包含有關支持的空間參考系統的數據。

QSpatialRefSys spatialRefSys = QSpatialRefSys.spatialRefSys;
List<SpatialRefSys> referenceSystems = query.select(spatialRefSys).from(spatialRefSys).fetch();

2.4.5. 繼承

如果您在數據庫模式中僅使用通用幾何類型,您可以使用對象模型中的轉換方法來轉換為更具體的類型。

GeometryPath<Geometry> geometry = shapes.geometry;
PointPath<Point> point = geometry.asPoint();
NumberExpression<Double> pointX = point.x(); // x() 在 GeometryExpression/GeometryPath 上不可用

2.5. 查詢 Lucene

本章介紹 Lucene 模塊的查詢功能。

2.5.1. Maven 集成

Querydsl Lucene 可以通過 Lucene 3 的 querydsl-lucene3 模塊、Lucene 4 的 querydsl-lucene4 和 Lucene 5 的 querydsl-lucene5 使用

Lucene 3:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-lucene3</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

Lucene 4:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-lucene4</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

Lucene 5:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-lucene5</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

2.5.2. 創建查詢類型

使用字段 year 和 title 手動創建的查詢類型可能如下所示:

public class QDocument extends EntityPathBase<Document> {
    private static final long serialVersionUID = -4872833626508344081L;

    public QDocument(String var) {
        super(Document.class, PathMetadataFactory.forVariable(var));
    }

    public final StringPath year = createString("year");

    public final StringPath title = createString("title");
}

QDocument 表示帶有字段 year 和 title 的 Lucene 文檔。

Lucene 不支持代碼生成,因為沒有可用的模式數據。

2.5.3. 查詢

使用 Querydsl Lucene 進行查詢就像這樣簡單:

QDocument doc = new QDocument("doc");

IndexSearcher searcher = new IndexSearcher(index);
LuceneQuery query = new LuceneQuery(true, searcher);
List<Document> documents = query
    .where(doc.year.between("1800", "2000").and(doc.title.startsWith("Huckle"))
    .fetch();

將其轉換為以下 Lucene 查詢:

+year:[1800 TO 2000] +title:huckle*

2.5.4. 一般用法

像這樣使用 LuceneQuery 類的級聯方法

where: 添加查詢過濾器,以可變參數形式通過逗號分隔或通過 and 運算符級聯。支持的操作是對除 matchesindexOfcharAt 之外的 PStrings 執行的操作。目前不支持 in,但將來會支持。

orderBy: 將結果的順序添加為順序表達式的可變參數數組。對數字、字符串和其他可比較的表達式使用 asc() 和 desc() 來訪問 OrderSpecifier 實例。

limit,offset,restrict: 設置結果的分頁。最大結果的限制,跳過行的偏移量和一次調用中定義兩者的限制。

2.5.5. Ordering

聲明排序的語法是

query
    .where(doc.title.like("*"))
    .orderBy(doc.title.asc(), doc.year.desc())
    .fetch();

相當於下面的 Lucene 查詢

title:*

結果根據標題和年份升序排序。

或者,可以使用排序方法調用將排序邏輯聲明為 Sort 實例

Sort sort = ...;
query
    .where(doc.title.like("*"))
    .sort(sort)
    .fetch();

2.5.6. Limit

聲明限制的語法是

query
    .where(doc.title.like("*"))
    .limit(10)
    .fetch();

2.5.7. Offset

聲明偏移量的語法是

query
    .where(doc.title.like("*"))
    .offset(3)
    .fetch();

2.5.8. 模糊搜索

模糊搜索可以通過 com.querydsl.lucene3.LuceneExpressions 類中的 blurLike 方法表示:

query
    .where(LuceneExpressions.fuzzyLike(doc.title, "Hello"))
    .fetch();

2.5.9. 將 Lucene 過濾器應用於查詢

可以將單個 Lucene 過濾器應用於查詢,如下所示:

query
    .where(doc.title.like("*"))
    .filter(filter)
    .fetch();

通過 distinct(Path) 方法提供不同過濾的快捷方式:

query
    .where(doc.title.like("*"))
    .distinct(doc.title)
    .fetch();

本章介紹 Hibernate Search 模塊的查詢功能。

2.6.1. 創建 Querydsl 查詢類型

有關如何創建查詢類型的說明,請參閱 查詢 JPA/Hibernate 源

2.6.2. 查詢

使用 Querydsl Hibernate Search 進行查詢非常簡單:

QUser user = QUser.user;
SearchQuery<User> query = new SearchQuery<User>(session, user);
List<User> list = query
    .where(user.firstName.eq("Bob"))
    .fetch();

2.6.3. 一般用法

有關一般使用說明,請參閱 查詢 Lucene 源

在查詢序列化中,與 Querydsl Lucene 模塊的唯一區別是路徑的處理方式不同。對於 org.hibernate.search.annotations.Field 帶注釋的屬性,名稱屬性與屬性名稱一起用作字段名稱的后備。

2.7. 查詢MongoDB

本章介紹Mongodb 模塊的查詢功能。

2.7.1. Maven 集成

將以下依賴項添加到您的 Maven 項目中:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-mongodb</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

現在,配置生成 Querydsl 使用的查詢類型的 Maven APT 插件:

<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.morphia.MorphiaAnnotationProcessor</processor>
            </configuration>
          </execution>
        </executions>
      </plugin>
    ...
    </plugins>
  </build>
</project>

MorphiaAnnotationProcessor 查找使用 com.google.code.morphia.annotations.Entity 注釋注釋的域類型,並為它們生成 Querydsl 查詢類型。

運行全新安裝,您將在 target/generated-sources/java 中生成查詢類型。

如果您使用 Eclipse,請運行 mvn eclipse:eclipse 以更新您的 Eclipse 項目以包含 target/generated-sources/java 作為源文件夾。

現在您可以構建 Mongodb 查詢和查詢域模型的實例。

2.7.2。查詢

使用 Morphia 使用 Querydsl Mongodb 進行查詢就像這樣簡單:

Morphia morphia;
Datastore datastore;
// ...
QUser user = new QUser("user");
MorphiaQuery<User> query = new MorphiaQuery<User>(morphia, datastore, user);
List<User> list = query
    .where(user.firstName.eq("Bob"))
    .fetch();

2.7.3. 一般用法

像這樣使用 MongodbQuery 類的級聯方法

where: 添加查詢過濾器,以可變參數形式通過逗號分隔或通過 and 運算符級聯。支持的操作是對除 matchesindexOfcharAt 之外的 PStrings 執行的操作。目前不支持 in,但將來會支持。

orderBy: 將結果的順序添加為順序表達式的可變參數數組。對數字、字符串和其他可比較的表達式使用 asc() 和 desc() 來訪問 OrderSpecifier 實例。

limit,offset,restrict: 設置結果的分頁。最大結果的限制,跳過行的偏移量和一次調用中定義兩者的限制。

2.7.4. Ordering

聲明排序的語法是

query
    .where(doc.title.like("*"))
    .orderBy(doc.title.asc(), doc.year.desc())
    .fetch();

結果根據標題和年份升序排序。

2.7.5. Limit

聲明限制的語法是

query
    .where(doc.title.like("*"))
    .limit(10)
    .fetch();

2.7.6. Offset

聲明偏移量的語法是

query
    .where(doc.title.like("*"))
    .offset(3)
    .fetch();

2.7.7. 地理空間查詢

Double 類型數組 (Double[]) 通過 near 方法支持地理空間查詢:

query
    .where(geoEntity.location.near(50.0, 50.0))
    .fetch();

2.7.8. 僅選擇相關字段

要僅選擇相關字段,您可以使用重載的投影方法 fetch、iterate、fetchOne 和 fetchFirst 方法,如下所示

query
    .where(doc.title.like("*"))
    .fetch(doc.title, doc.path);

此查詢將僅加載文檔的標題和路徑字段。

2.8. 查詢集合

querydsl-collections 模塊可以與生成的查詢類型一起使用,也可以不使用。第一部分描述了沒有生成查詢類型的用法:

2.8.1. 沒有生成查詢類型的用法

要在不生成查詢類型的情況下使用 querydsl-collections,您需要使用 Querydsl 別名功能。這里有些例子。

首先,添加以下靜態導入:

// 需要訪問 Querydsl Collections API
import static com.querydsl.collections.CollQueryFactory.*;
// 需要,如果你使用 $-invocations
import static com.querydsl.core.alias.Alias.*;

現在為 Cat 類創建一個別名實例。只能為具有空構造函數的非最終類創建別名實例。確保你的班級有一個。

Cat 類型的別名實例及其 getter 調用通過將它們包裝到美元方法調用中而轉換為路徑。例如,調用 c.getKittens() 在內部轉換為 $ 方法內的屬性路徑 c.kittens

Cat c = alias(Cat.class, "cat");
for (String name : select($(c.getName())).from($(c),cats)
  .where($(c.getKittens()).size().gt(0))
  .fetch()) {
    System.out.println(name);
}

以下示例是前一個示例的變體,其中對列表大小的訪問發生在美元方法調用中。

Cat c = alias(Cat.class, "cat");
for (String name : select($(c.getName())).from($(c),cats)
  .where($(c.getKittens().size()).gt(0))
  .fetch()) {
    System.out.println(name);
}

別名的所有非原始和非最終類型屬性都是別名本身。因此,您可以級聯方法調用,直到在美元方法范圍內遇到原始或非最終類型(例如 java.lang.String)。

例如

$(c.getMate().getName())

在內部轉換為 c.mate.name,但是

$(c.getMate().getName().toLowerCase())

未正確轉換,因為未跟蹤 toLowerCase() 調用。

另請注意,您只能在別名類型上調用 getter、size()、contains(Object) 和 get(int)。所有其他調用都會引發異常。

2.8.2. 與生成的查詢類型一起使用

上面的例子可以用生成的表達式類型這樣表達

QCat cat = new QCat("cat");
for (String name : select(cat.name).from(cat,cats)
  .where(cat.kittens.size().gt(0))
  .fetch()) {
    System.out.println(name);
}

當您使用生成的查詢類型時,您將實例化表達式而不是別名實例,並直接使用屬性路徑,而無需任何美元方法包裝。

2.8.3. Maven 集成

將以下依賴項添加到您的 Maven 項目中:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-collections</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
</dependency>

如果您不使用 JPA 或 JDO,您可以通過使用 com.querydsl.core.annotations.QueryEntity 批注並將以下插件配置添加到您的 Maven 配置 (pom.xml) 中來為您的域類型生成表達式類型:

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

2.8.4. Ant 集成

將來自 full-deps 包的 jar 文件放在類路徑中,並使用以下任務生成 Querydsl 代碼:

    <!-- 基於 APT 的代碼生成 -->
    <javac srcdir="${src}" classpathref="cp">
      <compilerarg value="-proc:only"/>
      <compilerarg value="-processor"/>
      <compilerarg value="com.querydsl.apt.QuerydslAnnotationProcessor"/>
      <compilerarg value="-s"/>
      <compilerarg value="${generated}"/>
    </javac>

    <!-- 編譯 -->
    <javac classpathref="cp" destdir="${build}">
      <src path="${src}"/>
      <src path="${generated}"/>
    </javac>

src 替換為您的主源文件夾,將 generated 替換為生成的源文件夾,將 build 替換為目標文件夾。

2.8.5. Hamcrest 匹配器

Querydsl Collections 提供 Hamcrest 匹配器。有了這些進口

import static org.hamcrest.core.IsEqual.equalTo;
import static com.querydsl.collections.PathMatcher.hasValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

它們可以這樣使用:

Car car = new Car();
car.setHorsePower(123);

assertThat(car, hasValue($.horsePower));
assertThat(car, hasValue($.horsePower, equalTo(123)));

Hamcrest 匹配器由 Jeroen van Schagen 貢獻。

2.8.6. 與 Java 的 Eclipse 編譯器一起使用

如果 Querydsl Collections 與系統編譯器不可用的 JRE 一起使用,則還可以將 CollQuery 實例配置為使用 Eclipse Compiler for Java (ECJ):

DefaultEvaluatorFactory evaluatorFactory = new DefaultEvaluatorFactory(
    CollQueryTemplates.DEFAULT,
    new ECJEvaluatorFactory(getClass().getClassLoader()));
QueryEngine queryEngine = new DefaultQueryEngine(evaluatorFactory);
CollQuery query = new CollQuery(queryEngine);

2.9. 在 Scala 中查詢

可以通過 querydsl-scala 模塊獲得對 Scala 中 Querydsl 使用的通用支持。要將其添加到您的 Maven 構建中,請使用以下代碼段:

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-scala</artifactId>
  <version>${querydsl.version}</version>
</dependency>

2.9.1. Scala 的 DSL 表達式

Querydsl for Scala 為表達式構造提供了另一種 DSL。Scala DSL 利用諸如運算符重載、函數指針和隱式導入等語言特性來增強可讀性和簡潔性。

以下是主要替代方案的概述:

//標准                  替代

expr isNotNull          expr is not(null)
expr isNull             expr is null
expr eq "Ben"           expr === "Ben"
expr ne "Ben"           expr !== "Ben"
expr append "X"         expr + "X"
expr isEmpty            expr is empty
expr isNotEmpty         expr not empty

// 布爾值
left and right          left && right
left or right           left || right
expr not                !expr

// 比較
expr lt 5               expr < 5
expr loe 5              expr <= 5
expr gt 5               expr > 5
expr goe 5              expr >= 5
expr notBetween(2,6)    expr not between (2,6)
expr negate             -expr

// 數字
expr add 3              expr + 3
expr subtract 3         expr - 3
expr divide 3           expr / 3
expr multiply 3         expr * 3
expr mod 5              expr % 5

// 集合
list.get(0)             list(0)
map.get("X")            map("X")

2.9.2. 使用 SQL 查詢

與 Querydsl SQL for Java 一樣,您需要生成查詢類型才能構建查詢。以下代碼示例顯示了這是如何完成的:

沒有 Bean 類型的生成:

val directory = new java.io.File("target/jdbcgen1")
val namingStrategy = new DefaultNamingStrategy()
val exporter = new MetaDataExporter()
exporter.setNamePrefix("Q")
exporter.setPackageName("com.querydsl")
exporter.setSchemaPattern("PUBLIC")
exporter.setTargetFolder(directory)
exporter.setSerializerClass(classOf[ScalaMetaDataSerializer])
exporter.setCreateScalaSources(true)
exporter.setTypeMappings(ScalaTypeMappings.create)
exporter.export(connection.getMetaData)

使用 Bean 類型生成:

val directory = new java.io.File("target/jdbcgen2")
val namingStrategy = new DefaultNamingStrategy()
val exporter = new MetaDataExporter()
exporter.setNamePrefix("Q")
exporter.setPackageName("com.querydsl")
exporter.setSchemaPattern("PUBLIC")
exporter.setTargetFolder(directory)
exporter.setSerializerClass(classOf[ScalaMetaDataSerializer])
exporter.setBeanSerializerClass(classOf[ScalaBeanSerializer])
exporter.setCreateScalaSources(true)
exporter.setTypeMappings(ScalaTypeMappings.create)
exporter.export(connection.getMetaData)
2.9.2.1。代碼生成

SQL 元類型和投影的 Scala 源可以使用 querydsl-maven-plugin 生成。這是一個示例配置

<project>
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-maven-plugin</artifactId>
        <version>${querydsl.version}</version>
        <configuration>
          <jdbcDriver>com.mysql.jdbc.Driver</jdbcDriver>
          <jdbcUrl>jdbc:mysql://localhost:3306/test</jdbcUrl>
          <jdbcUser>matko</jdbcUser>
          <jdbcPassword>matko</jdbcPassword>
          <packageName>com.example.schema</packageName>
          <targetFolder>${project.basedir}/src/main/scala</targetFolder>
          <exportBeans>true</exportBeans>
          <createScalaSources>true</createScalaSources>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.16</version>
          </dependency>
          <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>
      </plugin>
      ...
    </plugins>
  </build>
</project>

執行的 maven 目標是 querydsl:export。

2.9.3. 使用其他后端查詢

當使用其他后端查詢時,必須手動創建表達式模型,或者可以使用別名功能。

這是 JPA 的一個最小示例:

@Entity
class User {
  @BeanProperty
  @Id
  var id: Integer = _;
  @BeanProperty
  var userName: String = _;
  @BeanProperty
  @ManyToOne
  var department: Department = _;
}

@Entity
class Department {
  @BeanProperty
  @Id
  var id: Integer = _;
  @BeanProperty
  var name: String = _;
}

以下是一些查詢示例

列表

val person = Person as "person"

selectFrom(person).where(person.firstName like "Rob%").fetch()

唯一的結果

selectFrom(person).where(person.firstName like "Rob%").fetchOne()

長的 where 語句

selectFrom(person)
  .where(person.firstName like "Rob%", person.lastName like "An%")
  .fetch()

排序

selectFrom(person).orderBy(person.firstName asc).fetch()

不為空

selectFrom(person)
  .where(person.firstName isEmpty, person.lastName isNotNull)
  .fetch()

查詢創建的工廠方法是

def query() = new JPAQuery(entityManager)

除了查詢之外,您還需要可以像這樣創建的變量

val person = Person as "person"

注意:如果您將 Hibernate 與基於 XML 的配置一起使用,則 Scala 支持尚不可用。HibernateDomainExporter 目前只輸出 Java 源文件。

3. 一般用法

一般用法部分涵蓋了參考文檔的教程部分未涵蓋的方面。它遵循面向用例的結構。

3.1. 創建查詢

Querydsl 中的查詢構造涉及使用表達式參數調用查詢方法。由於查詢方法大多是特定於模塊的,並且已經在教程部分中介紹過,因此本部分將重點介紹表達式。

表達式通常是通過訪問域模塊生成的表達式類型上的字段和調用方法來構造的。對於代碼生成不適用的情況,可以改用構造表達式的通用方法。

3.1.1. 復雜謂詞

要構造復雜的布爾表達式,請使用 com.querydsl.core.BooleanBuilder 類。它實現了 Predicate 並且可以以級聯形式使用:

public List<Customer> getCustomer(String... names) {
    QCustomer customer = QCustomer.customer;
    JPAQuery<Customer> query = queryFactory.selectFrom(customer);
    BooleanBuilder builder = new BooleanBuilder();
    for (String name : names) {
        builder.or(customer.name.eq(name));
    }
    query.where(builder); // customer.name eq name1 OR customer.name eq name2 OR ...
    return query.fetch();
}

BooleanBuilder 是可變的,最初表示 null 並在每個 andor 調用之后表示操作的結果。

3.1.2. 動態表達式

com.querydsl.core.types.dsl.Expressions 類是用於動態表達式構造的靜態工廠類。工廠方法由返回的類型命名,並且大多是自記錄的。

一般來說,Expressions 類應該只在不能使用流暢的 DSL 形式的情況下使用,例如動態路徑、自定義語法或自定義操作。

下面的表達式

QPerson person = QPerson.person;
person.firstName.startsWith("P");

如果 Q-types 不可用,可以這樣構造

Path<Person> person = Expressions.path(Person.class, "person");
Path<String> personFirstName = Expressions.path(String.class, person, "firstName");
Constant<String> constant = Expressions.constant("P");
Expressions.predicate(Ops.STARTS_WITH, personFirstName, constant);

Path 實例表示變量和屬性,Constant 是常量,Operations 是操作,TemplateExpression 實例可用於將表達式表達為字符串模板。

3.1.3. 動態路徑

除了基於 Expressions 的表達式創建之外,Querydsl 還為動態路徑創建提供了更流暢的 API。

對於動態路徑生成,可以使用 com.querydsl.core.types.dsl.PathBuilder 類。它擴展了“EntityPathBase”,可以用作類生成和路徑生成別名使用的替代方案。

與 Expressions API 相比,PathBuilder 不提供對未知操作或自定義語法的直接支持,但語法更接近於普通的 DSL。

字符串屬性:

PathBuilder<User> entityPath = new
PathBuilder<User>(User.class, "entity");
// 完全通用的訪問
entityPath.get("用戶名");
// .. 或使用提供的類型
entityPath.get("userName", String.class);
// .. 和正確的簽名
entityPath.getString("userName").lower();

列出具有組件類型的屬性:

entityPath.getList("list", String.class).get(0);

使用組件表達式類型:

entityPath.getList("list", String.class, StringPath.class).get(0).lower();

具有鍵和值類型的映射屬性:

entityPath.getMap("map", String.class, String.class).get("key");

使用組件表達式類型:

entityPath.getMap("map", String.class, String.class, StringPath.class).get("key").lower();

對於 PathBuilder 驗證,可以使用 PathBuilderValidator。它可以被注入到構造函數中,並將被傳遞給新的 PathBuilder

PathBuilder<Customer> customer = new PathBuilder<Customer>(Customer.class, "customer", validator);

PathBuilderValidator.FIELDS 將驗證字段是否存在,PathBuilderValidator.PROPERTIES 驗證 Bean 屬性,而 JPAPathBuilderValidator 使用 JPA 元模型進行驗證。

3.1.4. Case 表達式

要構造 case-when-then-else 表達式,請像這樣使用“CaseBuilder”類:

QCustomer customer = QCustomer.customer;
Expression<String> cases = new CaseBuilder()
    .when(customer.annualSpending.gt(10000)).then("Premier")
    .when(customer.annualSpending.gt(5000)).then("Gold")
    .when(customer.annualSpending.gt(2000)).then("Silver")
    .otherwise("Bronze");
// case 表達式現在可以在投影或條件中使用

對於具有等號操作的 case 表達式,請改用以下更簡單的形式:

QCustomer customer = QCustomer.customer;
Expression<String> cases = customer.annualSpending
    .when(10000).then("Premier")
    .when(5000).then("Gold")
    .when(2000).then("Silver")
    .otherwise("Bronze");
// case 表達式現在可以在投影或條件中使用

JDOQL 中尚不支持案例表達式。

3.1.5. Casting 表達式

為了避免表達式類型中的泛型簽名,類型層次結構被展平。結果是所有生成的查詢類型都是 com.querydsl.core.types.dsl.EntityPathBase 或 com.querydsl.core.types.dsl.BeanPath 的直接子類,並且不能直接轉換為它們的邏輯超類型。

超類型引用不是直接的 Java 轉換,而是可以通過 _super 字段訪問。_super-field 在所有生成的查詢類型中都可用,只有一個超類型:

// 來自賬戶
QAccount 擴展 EntityPathBase<Account> {
    // ...
}

// 從 BankAccount 擴展 Account
QBankAccount extends EntityPathBase<BankAccount> {

    public final QAccount _super = new QAccount(this);

    // ...
}

要將超類型轉換為子類型,您可以使用 EntityPathBase 類的 as 方法:

QAccount account = new QAccount("account");
QBankAccount bankAccount = account.as(QBankAccount.class);

3.1.6. 選擇文字

可以通過常量表達式引用它們來選擇文字。這是一個簡單的例子

query.select(Expressions.constant(1),
             Expressions.constant("abc"));

常量表達式經常用在子查詢中。

3.2. 結果處理

Querydsl 提供了兩種自定義結果的方法,FactoryExpressions 用於基於行的轉換和 ResultTransformer 用於聚合。

com.querydsl.core.types.FactoryExpression 接口用於 Bean 創建、構造函數調用和更復雜對象的創建。可以通過 com.querydsl.core.types.Projections 類訪問 Querydsl 的 FactoryExpression 實現的功能。

對於 com.querydsl.core.ResultTransformer 接口,GroupBy 是主要實現。

3.2.1. 返回多列

從 Querydsl 3.0 開始,多列結果的默認類型是 com.querydsl.core.Tuple。Tuple 提供了一個類型安全的 Map 類接口來訪問 Tuple 行對象中的列數據。

List<Tuple> result = query.select(employee.firstName, employee.lastName)
                          .from(employee).fetch();
for (Tuple row : result) {
     System.out.println("firstName " + row.get(employee.firstName));
     System.out.println("lastName " + row.get(employee.lastName));
}}

這個例子也可以像這樣通過 QTuple 表達式類編寫

List<Tuple> result = query.select(new QTuple(employee.firstName, employee.lastName))
                          .from(employee).fetch();
for (Tuple row : result) {
     System.out.println("firstName " + row.get(employee.firstName));
     System.out.println("lastName " + row.get(employee.lastName));
}}

3.2.2. Bean 種群

在需要根據查詢結果填充 Bean 的情況下,可以像這樣使用 Bean 投影

List<UserDTO> dtos = query.select(
    Projections.bean(UserDTO.class, user.firstName, user.lastName)).fetch();

當應該直接使用字段而不是設置器時,可以使用以下變體

List<UserDTO> dtos = query.select(
    Projections.fields(UserDTO.class, user.firstName, user.lastName)).fetch();

3.2.3. 構造函數用法

可以像這樣使用基於構造函數的行轉換

List<UserDTO> dtos = query.select(
    Projections.constructor(UserDTO.class, user.firstName, user.lastName)).fetch();

作為通用構造函數表達式用法的替代方法,構造函數也可以使用 QueryProjection 注釋進行注釋:

class CustomerDTO {

  @QueryProjection
  public CustomerDTO(long id, String name) {
     ...
  }

}

然后你可以在查詢中像這樣使用它

QCustomer customer = QCustomer.customer;
JPQLQuery query = new HibernateQuery(session);
List<CustomerDTO> dtos = query.select(new QCustomerDTO(customer.id, customer.name))
                              .from(customer).fetch();

雖然該示例是特定於 Hibernate 的,但此功能在所有模塊中都可用。

如果帶有 QueryProjection 注釋的類型不是帶注釋的實體類型,則可以像示例中那樣使用構造函數投影,但如果帶注釋的類型是實體類型,則需要通過調用來創建構造函數投影查詢類型的靜態創建方法:

@Entity
class Customer {

  @QueryProjection
  public Customer(long id, String name) {
     ...
  }

}
QCustomer customer = QCustomer.customer;
JPQLQuery query = new HibernateQuery(session);
List<Customer> dtos = query.select(QCustomer.create(customer.id, customer.name))
                           .from(customer).fetch();

或者,如果代碼生成不是一個選項,您可以創建一個構造函數投影,如下所示:

List<Customer> dtos = query
    .select(Projections.constructor(Customer.class, customer.id, customer.name))
    .from(customer).fetch();

3.2.4. 結果聚合

com.querydsl.core.group.GroupBy 類提供聚合功能,可用於在內存中聚合查詢結果。下面是一些使用示例。

聚合父子關系

import static com.querydsl.core.group.GroupBy.*;

Map<Integer, List<Comment>> results = query.from(post, comment)
    .where(comment.post.id.eq(post.id))
    .transform(groupBy(post.id).as(list(comment)));

這將返回一個帖子 ID 到相關評論的地圖。

多個結果列

Map<Integer, Group> results = query.from(post, comment)
    .where(comment.post.id.eq(post.id))
    .transform(groupBy(post.id).as(post.name, set(comment.id)));

這會將帖子 ID 的映射返回到可以訪問帖子名稱和評論 ID 的 Group 實例。

Group 是 GroupBy 等價於 Tuple 接口。

更多示例可以在 這里 找到。

3.3. 代碼生成

Java 6 APT 注釋處理功能在 Querydsl 中用於 JPA、JDO 和 Mongodb 模塊中的代碼生成。本節介紹代碼生成的各種配置選項以及 APT 使用的替代方法。

3.3.1. 路徑初始化

默認情況下,Querydsl 只初始化前兩個級別的引用屬性。如果需要更長的初始化路徑,則必須通過 com.querydsl.core.annotations.QueryInit 注釋在域類型中進行注釋。QueryInit 用於需要深度初始化的屬性。下面的例子演示了用法。

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

@Entity
class Account {
    Customer customer;
}

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

當事件路徑被初始化為根路徑/變量時,此示例強制執行 account.customer 路徑的初始化。路徑初始化格式也支持通配符,例如“customer.”或只是“”。

自動路徑初始化取代了手動初始化,后者要求實體字段是非最終的。聲明性格式的好處是可以應用於 Query 類型的所有頂級實例並啟用最終實體字段的使用。

自動路徑初始化是首選的初始化策略,但可以通過 Config 注解激活手動初始化,如下所述。

3.3.2. 定制

Querydsl 的序列化可以通過包和類型上的 Config 注釋來定制。他們自定義帶注釋的包或類型的序列化。

序列化選項是

表 3.1. 配置選項

姓名 說明
entityAccessors 實體路徑而不是公共最終字段的訪問器方法(默認值:false)
listAccessors listProperty(int index) 樣式方法(默認值:false)
mapAccessors mapProperty(Key key) 樣式訪問器方法(默認值:false)
createDefaultVariable 生成默認變量(默認值:true)
defaultVariableName 默認變量的名稱

下面是一些例子。

Entity類型序列化的自定義:

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

包內容定制:

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

import com.querydsl.core.annotations.Config;

如果要全局自定義序列化器配置,可以通過以下 APT 選項來實現

表 3.2。APT 選項

姓名 說明
querydsl.entityAccessors 啟用引用字段訪問器
querydsl.listAccessors 為直接索引列表訪問啟用訪問器
querydsl.mapAccessors 為基於直接鍵的映射訪問啟用訪問器
querydsl.prefix 覆蓋查詢類型的前綴(默認:Q)
querydsl.suffix 為查詢類型設置后綴
querydsl.packageSuffix 為查詢類型包設置后綴
querydsl.createDefaultVariable 設置是否創建默認變量
querydsl.unknownAsEmbeddable 設置未知的非注釋類應被視為可嵌入的位置(默認值:false)
querydsl.includedPackages 要包含在代碼生成中的包的逗號分隔列表(默認值:全部)
querydsl.includedClasses 要包含在代碼生成中的類名的逗號分隔列表(默認值:全部)
querydsl.excludedPackages 要從代碼生成中排除的包的逗號分隔列表(默認值:無)
querydsl.excludedClasses 要從代碼生成中排除的類名的逗號分隔列表(默認值:無)
querydsl.useFields 設置字段是否用作元數據源(默認:true)
querydsl.useGetters 設置訪問器是否用作元數據源(默認值:true)

使用 Maven APT 插件,例如這樣工作:

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

或者,可以將 maven-compiler-plugin 配置為將 APT 直接掛接到編譯中:

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

請注意,在定義對 com.querydsl:querydsl-apt 的依賴時,您需要使用適當的分類器。這些額外的工件定義了要在 META-INF/services/javax.annotation.processing.Processor 中使用的注釋處理器。

可用的分類器包括:

  • general
  • hibernate
  • jdo
  • jpa

使用此配置,查詢對象可以在域對象的編譯期間生成和編譯它們的源。這也會自動將生成的源目錄添加到 Maven 項目源根目錄。

這種方法的最大優點是它還可以使用 groovy-eclipse 編譯器處理帶注釋的 Groovy 類:

<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。自定義類型映射

自定義類型映射可用於屬性以覆蓋派生的 Path 類型。例如,在某些字符串路徑上應阻止比較和字符串操作或需要添加對自定義類型的日期/時間支持的情況下,這可能很有用。內置了對 Joda 時間 API 和 JDK(java.util.Date、Calendar 和子類型)的日期/時間類型的支持,但使用此功能可能需要支持其他 API。

以下示例演示了用法:

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

    @QueryType(PropertyType.COMPARABLE)
    public String stringAsComparable;

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

PropertyType.NONE 可用於在查詢類型生成中跳過屬性。這種情況與 @Transient 或 @QueryTransient 注釋屬性不同,其中屬性不會持久化。PropertyType.NONE 只是省略了 Querydsl 查詢類型中的屬性。

3.3.4. 委托方法

要將靜態方法聲明為委托方法,請添加具有相應域類型作為值的 QueryDelegate 注釋,並提供將相應 Querydsl 查詢類型作為第一個參數的方法簽名。

下面是一個來自單元測試的簡單示例:

@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);
}

以及 QUser 查詢類型中生成的方法:

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

委托方法也可用於擴展內置類型。這里有些例子

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())));
    }

}

當為內置類型聲明委托方法時,將創建具有正確委托方法用法的子類:

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. 非注釋類型

可以通過創建 @QueryEntities 注釋來為非注釋類型創建 Querydsl 查詢類型。只需將 QueryEntities 注釋放入您選擇的包中,並在 value 屬性中鏡像類。

要實際創建類型,請使用 com.querydsl.apt.QuerydslAnnotationProcessor。在 Maven 中,您可以這樣做:

<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. 基於類路徑的代碼生成

對於注釋 Java 源不可用的情況,例如使用不同的 JVM 語言(如 Scala 或 Groovy)或通過字節碼操作添加注釋,GenericExporter 類可用於掃描類路徑以查找帶注釋的類並生成查詢類型他們。

要使 GenericExporter 可用,請將 querydsl-codegen 模塊的依賴項添加到您的項目中,或者更准確地說是“com.querydsl:querydsl-codegen:${querydsl.version}”。

以下是 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());

這會將 DomainClass 類和子包的包中的所有 JPA 注釋類導出到 target/generated-sources/java 目錄。

3.3.6.1. 通過 Maven 使用

querydsl-maven-plugin 的目標 generic-export、jpa-export 和 jdo-export 可用於通過 Maven 使用 GenericExporter。

不同的目標映射到 Querydsl、JPA 和 JDO 注釋。

配置元素是

表 3.3. Maven 配置

類型 元素 說明
File targetFolder 生成源的目標文件夾
boolean scala true,如果應該生成 Scala 源代碼(默認值:false)
String[] packages 為實體類自省的包
boolean handleFields true,如果字段應被視為屬性(默認值:true)
boolean handleMethods true,如果 getter 應被視為屬性(默認值:true)
String sourceEncoding 生成的源文件的字符集編碼
boolean testClasspath true,如果應該使用測試類路徑

這是 JPA 注釋類的示例

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

這會將 com.example.domain 包和子包的 JPA 注釋類導出到 target/generated-sources/java 目錄。

如果您需要在此之后直接編譯生成的源代碼,那么您可以使用 compile 目標。

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

compile 目標具有以下配置元素

表 3.4. Maven 配置

類型 元素 說明
File sourceFolder 帶有生成源的源文件夾
String sourceEncoding 來源的字符集編碼
String source -source 編譯器選項
String target -target 編譯器選項
boolean testClasspath true,如果應該使用測試類路徑
Map compilerOptions 編譯器選項

除了 sourceFolder 之外的所有選項都是可選的。

3.3.6.2. Scala 支持

如果您需要類的 Scala 輸出,請使用以下配置的變體

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

3.4. 別名用法

在不能選擇代碼生成的情況下,別名對象可以用作表達式構造的路徑引用。它們可以通過 getter 方法調用通過代理 Java Bean 對象使用。

以下示例演示如何使用別名對象作為基於生成的類型創建表達式的替代品。

首先是使用 APT 生成的域類型的示例查詢:

QCat cat = new QCat("cat");
for (String name : queryFactory.select(cat.name).from(cat,cats)
    .where(cat.kittens.size().gt(0))
    .fetch()) {
    System.out.println(name);
}

現在有了 Cat 類的別名實例。美元方法內的調用 c.getKittens() 在內部轉換為屬性路徑 c.kittens

Cat c = alias(Cat.class, "cat");
for (String name : select($(c.getName())).from($(c),cats)
    .where($(c.getKittens()).size().gt(0))
    .fetch()) {
    System.out.println(name);
}

要在代碼中使用別名功能,請添加以下兩個導入

import static com.querydsl.core.alias.Alias.$;
import static com.querydsl.core.alias.Alias.alias;

以下示例是前一個示例的變體,其中對列表大小的訪問發生在美元方法調用中。

Cat c = alias(Cat.class, "cat");
for (String name : queryFactory.select($(c.getName())).from($(c),cats)
    .where($(c.getKittens().size()).gt(0))
    .fetch()) {
    System.out.println(name);
}

別名的所有非原始和非最終類型屬性都是別名本身。因此,您可以級聯方法調用,直到在美元方法范圍內達到原始類型或最終類型。例如

$(c.getMate().getName())

在內部轉換為 c.mate.name,但是

$(c.getMate().getName().toLowerCase())

未正確轉換,因為未跟蹤 toLowerCase() 調用。

另請注意,您只能在別名類型上調用 getter、size()、contains(Object) 和 get(int)。所有其他調用都會引發異常。

4. 故障排除

4.1. 類型參數不足

Querydsl 在所有代碼生成場景中都需要正確編碼的 List Set、Collection 和 Map 屬性。

當使用不正確編碼的字段或 getter 時,您可能會出現以下堆棧跟蹤:

java.lang.RuntimeException: Caught exception for field com.querydsl.jdo.testdomain.Store#products
  at com.querydsl.apt.Processor$2.visitType(Processor.java:117)
  at com.querydsl.apt.Processor$2.visitType(Processor.java:80)
  at com.sun.tools.javac.code.Symbol$ClassSymbol.accept(Symbol.java:827)
  at com.querydsl.apt.Processor.getClassModel(Processor.java:154)
  at com.querydsl.apt.Processor.process(Processor.java:191)
  ...
Caused by: java.lang.IllegalArgumentException: Insufficient type arguments for List
  at com.querydsl.apt.APTTypeModel.visitDeclared(APTTypeModel.java:112)
  at com.querydsl.apt.APTTypeModel.visitDeclared(APTTypeModel.java:40)
  at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:696)
  at com.querydsl.apt.APTTypeModel.<init>(APTTypeModel.java:55)
  at com.querydsl.apt.APTTypeModel.get(APTTypeModel.java:48)
  at com.querydsl.apt.Processor$2.visitType(Processor.java:114)
  ... 35 more

有問題的字段聲明及其更正示例:

    private Collection names; // WRONG

    private Collection<String> names; // RIGHT

    private Map employeesByName; // WRONG

    private Map<String,Employee> employeesByName; // RIGHT

4.2. Querydsl Q 類型的多線程初始化

當從多個線程初始化 Querydsl Q-types 時,如果 Q-types 具有循環依賴關系,則可能會發生死鎖。

一個易於使用的解決方案是在將類用於不同線程之前在單個線程中對其進行初始化。

com.querydsl.codegen.ClassPathUtils 類可以像這樣使用:

    ClassPathUtils.scanPackage(Thread.currentThread().getContextClassLoader(), packageToLoad);

將 packageToLoad 替換為要初始化的類的包。

4.3. JDK5使用

使用 JDK 5 編譯項目時,您可能會遇到以下編譯失敗:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
...
class file has wrong version 50.0, should be 49.0

Java 6.0 使用類文件版本 50.0,Java 5.0 使用 49.0。

Querydsl 僅針對 JDK 6.0 進行測試,因為我們廣泛使用 APT,而 APT 僅在 JDK 6.0 之后才可用。

如果您想在 JDK 5.0 中使用它,您可能想嘗試自己編譯 Querydsl。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM