概述
Ebean 提供了一种通过生成查询 Bean
来构建类型安全查询的机制。查询 Bean
和类型安全查询构造的设计/方法面向最大可读性,并且为了用 Java(不支持属性)实现这一点,这意味着我们需要使用 AOP 来增强查询 Bean
。AOP 增强可以通过以下方式完成
IntelliJ IDEA 插件
- 非常适合在开发期间使用javaagent
- 在开发期间“还行”,适合生产部署maven 插件
- 适合生产部署
建议
建议在开发期间使用IntelliJ IDEA 插件
,因为相对于在运行测试等时指定javaagent
而言,它非常易于使用。
对于部署/生产,可以使用javaagent
或maven 插件
。
优点
模型更改
当实体 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();
}
}