在 JPA Criteria API 之上构建更简洁的投影层

发布: (2026年1月12日 GMT+8 22:37)
3 分钟阅读
原文: Dev.to

Source: Dev.to

介绍

如果你使用 Java 和 JPA 开发系统,可能已经遇到过只需要返回给定实体的部分属性的查询需求。
乍一看这似乎很简单,但如果处理不当,系统可能会累积不必要的查询,或出现包含永远不会使用的属性的查询。

在许多真实场景中,开发者只需要获取实体的 idname。由于系统规模和复杂度,往往难以判断是否已经存在返回恰好这些数据的查询。还有的情况下,开发者会重复使用加载整个实体的方法,随后再提取实际需要的少量属性。

ProjectionQuery 的创建旨在简化和组织基于投影的查询,提供一种更清晰、更具表达力的方式,只选择应用真正需要的数据。

定义投影

在项目中添加依赖后,创建一个类(或 record),并为需要投影的字段加上注解。

@Projection(of = Customer.class)
public record CustomerBasicData(
        @ProjectionField Long id,
        @ProjectionField String name,
        @ProjectionField("address.city.name") String city,
        @ProjectionField("address.city.state.name") String state
) { }

该 record 表示查询结果的最终结构。无论 Customer 实体中有多少属性,只有 idnamecitystate 会从数据库中被选取。
需要注意的是,citystate 是通过 Customer 实体中定义的关系获取的嵌套属性。

生成的 SQL(简化)

上述投影对应的 SQL 大致如下:

SELECT
    c.id,
    c.name,
    city.name   AS city,
    state.name  AS state
FROM customer c
INNER JOIN address a   ON c.address = a.id
INNER JOIN city   ci  ON a.city = ci.id
INNER JOIN state  s   ON ci.state = s.id;

执行投影

简化执行

ProjectionProcessor processor = new ProjectionProcessor(entityManager);
List customers = processor.execute(CustomerBasicData.class);

在高级场景中使用 ProjectionQuery

ProjectionQuery 帮助构建更复杂的查询,支持添加过滤、排序、分页以及其他配置。

ProjectionProcessor processor = new ProjectionProcessor(entityManager);

ProjectionQuery query = ProjectionQuery
    .fromTo(Customer.class, CustomerBasicData.class)
    .filter("address.city.name", ProjectionFilterOperator.EQUAL, "São Paulo")
    .order("name", OrderDirection.ASC)
    .paging(0, 20)
    .distinct();

List customers = processor.execute(query);

ProjectionQuery 可以独立使用,也可以集成到 Spring Boot 应用中。

更多资源

欲了解更多细节、示例以及完整文档,请访问 GitHub 上的项目页面。

Back to Blog

相关文章

阅读更多 »

Java 变量

什么是 Java 中的变量?变量是用于存储数据值的容器。Java 中变量的类型 本地变量 示例:java class Test { void dis...