You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用CriteriaQuery实现数组聚合并查询自定义数据访问类?

问题描述

实体类定义:

class User {
  private Long id;
  private String name;
  private Set<Group> groups;
  // 省略其他代码
}

class Group {
  private Long id;
  private String name;
  // 省略其他代码
}

希望构建等效于以下SQL的CriteriaQuery:

select user.id, user.name, ARRAY_AGG(user_groups.groups_id) 
from user 
left join user_groups ON user_groups.user_id = user.id 
group by user.id

尝试的代码及遇到的问题:

  1. 初始代码(修正原笔误ident为id):
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery query = criteriaBuilder.createQuery(CustomUser.class);
Root<User> entryRoot = query.from(User.class);
query.groupBy(entryRoot.get("id"));
  1. 直接在multiselect中加入join失败,因缺少聚合函数:
query.multiselect(entryRoot.get("id"), entryRoot.get("name"), entryRoot.join("groups", JoinType.LEFT));
  1. 尝试用criteriaBuilder.array创建数组选择失败,因multiselect不允许CompoundSelection与其他Selection混用:
CompoundSelection<Object[]> arraySelection = criteriaBuilder.array(entryRoot.join("groups", JoinType.LEFT));
query.multiselect(entryRoot.get("id"), entryRoot.get("name"), arraySelection);

需实现数组聚合,且必须使用CustomUser.class作为查询返回类型,以便后续扩展反向关联实体的连接逻辑。

解决方案

要实现等效于ARRAY_AGG的聚合查询,需借助JPA的CriteriaBuilder.function方法调用数据库原生聚合函数,同时正确映射到CustomUser类。

步骤1:匹配CustomUser类结构

CustomUser需包含对应字段及匹配的构造方法:

class CustomUser {
  private Long id;
  private String name;
  private List<Long> groupIds; // 对应ARRAY_AGG的结果

  // 构造方法需与multiselect字段顺序、类型完全一致
  public CustomUser(Long id, String name, List<Long> groupIds) {
    this.id = id;
    this.name = name;
    this.groupIds = groupIds;
  }

  // 省略getter/setter
}

步骤2:构建CriteriaQuery

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<CustomUser> query = cb.createQuery(CustomUser.class);
Root<User> userRoot = query.from(User.class);

// 左连接groups关联,获取group的id字段
Join<User, Group> groupJoin = userRoot.join("groups", JoinType.LEFT);

// 调用数据库原生ARRAY_AGG函数,聚合group的id
Expression<List<Long>> groupIdsAgg = cb.function(
    "ARRAY_AGG",
    List.class,
    groupJoin.get("id")
);

// 分组条件:需包含所有非聚合的选择字段
query.groupBy(userRoot.get("id"), userRoot.get("name"));

// 多字段选择,对应CustomUser构造方法
query.multiselect(
    userRoot.get("id"),
    userRoot.get("name"),
    groupIdsAgg
);

// 执行查询
List<CustomUser> result = entityManager.createQuery(query).getResultList();

关键说明

  • 使用cb.function调用数据库原生ARRAY_AGG函数,参数依次为函数名、返回类型、函数入参(此处为group的id)。
  • 分组时必须包含所有非聚合的选择字段,否则会触发SQL分组逻辑错误。
  • 若数据库对ARRAY_AGG有特殊语法要求(如PostgreSQL中过滤空值、排序),可调整cb.function的入参,例如:cb.function("ARRAY_AGG", List.class, cb.coalesce(groupJoin.get("id"), cb.literal(0)))。

内容的提问来源于stack exchange,提问作者david

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:40:23