Querydsl Reference Guide(中文版):2. 教程

2. 教程

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

2.1. Querying JPA(查询JPA)

Querydsl 定义了一种通用的静态类型语法,用于在持久域模型数据之上进行查询。 JDO 和 JPA 是 Querydsl 的主要集成技术。 本指南介绍了如何将 Querydsl 与 JPA 结合使用。

Querydsl for JPA 是 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

运行maven clean install,您将得到生成的查询类到 target/generated-sources/java目录下。

如果您使用 Eclipse,请运行 mvn eclipse:eclipse 来更新您的 Eclipse 项目以包含 target/generated-sources/java 作为源文件夹。

现在您可以构建 JPA 查询实例和实体查询类的实例。

2.1.2. Ant集成

将 full-deps 包中的 jar 文件放在类路径上,并使用以下任务生成 Querydsl 代码:

    <!-- APT based code generation -->
    <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>

    <!-- compilation -->
    <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文件生成模型

如果您将 Hibernate 与基于 XML 的配置一起使用,您可以使用 XML 元数据来创建您的 Querydsl 模型。

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

HibernateDomainExporter exporter = new HibernateDomainExporter(
  "Q",                     // name prefix
  new File("target/gen3"), // target folder
  configuration);          // instance of org.hibernate.cfg.Configuration

exporter.export();

HibernateDomainExporter 需要在实体类型可见的类路径中执行,因为属性类型是通过反射解析的。

所有 JPA 注解都被忽略,但 Querydsl 注解如 @QueryInit@QueryType 被考虑在内。

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 将在与 Customer 相同的包中生成一个名称为 QCustomer 的查询类型。 QCustomer 可以用作 Querydsl 查询中的静态类型变量,作为 Customer 类型的代表。

QCustomer 有一个可以作为静态字段访问的默认实例变量:

QCustomer customer = QCustomer.customer;

或者,您可以像这样定义自己的 Customer 变量:

QCustomer customer = new QCustomer("myCustomer");

2.1.6. 查询

Querydsl JPA 模块支持 JPA 和 Hibernate API。

要使用 JPA API,您可以使用 JPAQuery 实例进行查询,如下所示:

// where entityManager is a JPA EntityManager
JPAQuery<?> query = new JPAQuery<Void>(entityManager);

如果您使用的是 Hibernate API,则可以像这样实例化一个 HibernateQuery

// where session is a Hibernate session
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();

查询的native 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: 以可变参数形式添加 group by 参数。

  • having: 添加具有“group by”分组的过滤器作为谓词表达式的 varags 数组。

  • orderBy: 将结果的排序添加为顺序表达式的可变参数数组。 在数字、字符串和其他可比较的表达式上使用 asc() 和 desc() 来访问 OrderSpecifier 实例。

  • limit, offset, restrict: 设置结果的分页。 最大结果的限制,跳过行的偏移量和在一次调用中定义两者的限制。

2.1.9. 排序

声明排序的语法是

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. 分组

可以按以下形式进行分组

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. 删除子句

Querydsl JPA 中的删除子句遵循简单的 delete-where-execute 形式。 这里有些例子:

QCustomer customer = QCustomer.customer;
// delete all customers
queryFactory.delete(customer).execute();
// delete all customers with a level less than 3
queryFactory.delete(customer).where(customer.level.lt(3)).execute();

where 调用是可选的,execute 调用执行删除并返回已删除实体的数量。

💡提示: JPA中的DML子句没有考虑JPA级别的级联规则,也不提供细粒度的二级缓存交互。

2.1.12. 更新子句

Querydsl JPA 中的更新子句遵循简单的 update-set/where-execute 形式。 这里有些例子:

QCustomer customer = QCustomer.customer;
// rename customers named Bob to Bobby
queryFactory.update(customer).where(customer.name.eq("Bob"))
    .set(customer.name, "Bobby")
    .execute();

set调用以sql -Update样式定义属性更新,而execute调用执行Update并返回更新的实体数量。

💡提示: JPA中的DML子句没有考虑JPA级别的级联规则,也不提供细粒度的二级缓存交互。

2.1.13. 子查询

要创建子查询,您可以使用JPAExpressions 的静态工厂方法并通过 fromwhere 等定义查询参数。

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. 使用原始JPA的Query

如果你需要在执行查询之前调整原始JPA Query(javax.persistence.Query),你可以像这样暴露它:

javax.persistence.Query jpaQuery = queryFactory.selectFrom(QEmployee.employee).createQuery();

// ...

@SuppressWarnings("unchecked")
List<Employee> results = (List<Employee>) jpaQuery.getResultList();

2.1.15. 在JPA查询中使用Native SQL

Querydsl通过JPASQLQuery类在JPA中支持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>

当查询类型成功生成到您选择的位置时,您可以在查询中使用它们。

单列查询:

// serialization templates
SQLTemplates templates = new DerbyTemplates();
// query types (S* for SQL, Q* for domain types)
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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
无法解析 com.querydsl:querydsl-jpa:${querydsl.version}是因为在引用中使用了变量${querydsl.version}。这种情况通常是在项目的pom.xml文件中定义了一个名为querydsl.version的属性,然后在引用中使用该属性的值。如果没有定义这个属性或者属性的值无法解析,就会出现无法解析的情况。为了解决这个问题,你可以在pom.xml文件中定义querydsl.version属性并赋予一个具体的值,或者直接在引用中使用具体的版本号。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [SpringBoot项目整合JPA+QueryDSL,及apt-maven-plugin报错问题解决](https://blog.csdn.net/weixin_42315600/article/details/103627304)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [java.lang.NoSuchMethodError: com.querydsl.jpa.JPQLSerializer.....Ljava/util jar冲突](https://blog.csdn.net/weixin_38175213/article/details/127266784)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值