全文已翻译完成,但由于博主精力有限,难免有错漏之处,请见谅(错漏问题可以记录在评论区)

目录

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.Query com.querydsl.core.Fetchable com.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. 参数

    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。数字映射

    注意:重命名表和列时可以省略 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 查询: