概述

生成

生成查询 Bean(通过 Java 注解处理器)

使用查询 Bean

使用查询 Bean 及其工作原理

 

Ebean 提供了一种通过生成查询 Bean来构建类型安全查询的机制。查询 Bean和类型安全查询构造的设计/方法面向最大可读性,并且为了用 Java(不支持属性)实现这一点,这意味着我们需要使用 AOP 来增强查询 Bean。AOP 增强可以通过以下方式完成

  • IntelliJ IDEA 插件 - 非常适合在开发期间使用
  • javaagent - 在开发期间“还行”,适合生产部署
  • maven 插件 - 适合生产部署

建议

建议在开发期间使用IntelliJ IDEA 插件,因为相对于在运行测试等时指定javaagent而言,它非常易于使用。

对于部署/生产,可以使用javaagentmaven 插件

优点

模型更改

当实体 Bean 模型发生更改时,查询 Bean 会重新生成。如果模型更改破坏了查询,则会导致编译错误(而不是运行时错误),因此可以轻松且及早地检测到破坏性模型更改。

类型化绑定值

使用查询 Bean 意味着用于任何给定属性的绑定值必须与属性类型兼容。例如,可以为具有时间戳类型的属性绑定字符串值。

开发速度

使用查询 Bean 意味着我们可以使用 IDE 自动完成来帮助编写查询。在构建查询时无需检查模型。这使得开发变得更快、更安全、更有趣!

长期维护

随着应用程序变得越来越大、越来越旧,使用强类型查询 Bean 可以简化维护,因为它有助于指导不太熟悉模型的新开发者,并且由于编译类型检查相对于基于字符串的查询,跨大型应用程序的模型更改风险要小得多。

示例

示例

List<Customer> customers =
  new QCustomer()
    .id.greaterThan(12)
    .name.startsWith("Rob")
    .findList();

示例:嵌套 or() 和 and()

以下示例包含一个包含嵌套 and()or()

List<Customer> customers
  = new QCustomer()
  .billingAddress.city.equalTo("Auckland")
  .version.between(1,100)
  .billingAddress.country.equalTo(nz)
  .registered.before(new Date())
  .or()
    .id.greaterThan(1)
    .and()
      .name.icontains("Jim")
      .inactive.isTrue()
    .endAnd()
  .endOr()
  .orderBy()
    .name.asc()
    .id.desc()
  .findList();

示例:filterMany

filterMany 提供了过滤关联的 OneToMany 或 ManyToMany 关系的返回对象的功能。

例如,假设客户有很多订单,对于特定用例,我们希望找到客户,并且只希望找到他们的近期新订单。我们想要获取客户的订单(OneToMany),但希望过滤这些订单,仅包含“近期新订单”

Date oneWeekAgo = ...

// Use QOrder to create an expression list ...
// .. use this expression list to then filter orders
ExpressionList<Order> recentNewOrders = new QOrder()
  .orderDate.after(oneWeekAgo)
  .status.eq(Order.Status.NEW)
  .getExpressionList();


List<Customer> customers = new QCustomer()
  .name.ilike("Rob")
  // filter the orders (not customers)
  .orders.filterMany(recentNewOrders)
  .findList();

示例:select() 和 fetch()

使用 alias() 方法获取 query bean 的共享实例,该实例可用于为 select()fetch() 指定属性。

请注意,由 alias() 方法返回的查询 Bean 的“别名”实例没有与之关联的基础查询,并且只能用于向 select()fetch() 提供属性。

// special 'alias' instances of query beans
// used to supply properties to the select() and fetch()
QCustomer cus = QCustomer.alias();
QContact con = QContact.alias();

List<Customer> customers = new QCustomer()
  // query tuning
  .select(cus.name, cus.inactive)
  .contacts.fetch(con.email, con.firstName)

  // predicates
  .name.ilike("Rob")
  .findList();
// special 'alias' instances of query beans
// used to supply properties to the select() and fetch()
QContact con = QContact.alias();
QCustomer cus = QCustomer.alias();
QProduct pro = QProduct.alias();

Customer customer =
  Customer.find.where()
    // manual query tuning
    .select(cus.name, cus.registered)
    .orders.fetchAll()
    .orders.details.product.fetch(pro.name)
    .contacts.fetch(con.firstName)
    // predicates
    .id.eq(12)
    .findOne();

APT / KAPT (生成查询 Bean)

要生成 Java 查询 Bean,我们使用 io.ebean:querybean-generator Java 注解处理器,要生成 Kotlin 查询 Bean,我们使用 io.ebean:kotlin-querybean-generator

使用 Maven 生成

有关如何使用 Maven 生成查询 Bean 的详细信息,请参阅 docs / getting-started / maven

使用 Gradle 生成

有关如何使用 Gradle 生成查询 Bean 的详细信息,请参阅 docs / getting-started / gradle

增强

请注意,在 12.1.8 版本之前,我们需要编辑 src/main/resources/ebean.mf 清单文件,为应针对查询 Bean 增强功能的包指定 querybean-packages。从 12.1.8 版本开始,不再需要执行此操作。


手动生成

有一种机制可以通过运行代码(而不是通过 javac 注解处理)生成查询 Bean。从某种意义上来说,这是手动的,因为每当实体 Bean 发生更改时,我们就需要重新运行此过程(而使用 Java 注解处理器,它会随着每次编译自动更新)。

<dependency>
  <groupId>io.ebean.tools</groupId>
  <artifactId>finder-generator</artifactId>
  <version>12.1.1</version>
</dependency>

要生成查询 bean,请将类似于以下内容的代码添加到您的src/test中。生成器使用ASM库读取已编译的.class实体 bean。然后,它使用此元数据为查询 bean生成 Java 源代码。

使用此方法而不是使用javac注释处理意味着不需要 javac - 您可以使用任何 JVM 语言来编写/编译实体 bean(因此可能是Scala和 SBT。

此方法的要求是实体 bean 被编译为 .class 文件,并且GeneratorConfig被配置为知道类的位置、源代码应该去的位置以及包含实体 bean 的包。

package main;

import org.avaje.ebean.typequery.generator.Generator;
import org.avaje.ebean.typequery.generator.GeneratorConfig;

import java.io.IOException;

/**
 * Generate type query beans for each entity bean.
 */
public class MainQueryBeanGenerator {

  public static void main(String[] args) throws IOException {

    GeneratorConfig config = new GeneratorConfig();
    //config.setClassesDirectory("./target/classes");
    //config.setDestDirectory("./src/main/java");
    //config.setDestResourceDirectory("./src/main/resources");

    config.setEntityBeanPackage("org.example.domain");
    //config.setDestPackage("org.example.domain.query");

    //config.setOverwriteExistingFinders(true);

    Generator generator = new Generator(config);
    generator.generateQueryBeans();

    // Additionally generate 'finder's
    //generator.generateFinders();
    //generator.modifyEntityBeansAddFinderField();
  }
}