如何使用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
尝试的代码及遇到的问题:
- 初始代码(修正原笔误
ident为id):
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery query = criteriaBuilder.createQuery(CustomUser.class); Root<User> entryRoot = query.from(User.class); query.groupBy(entryRoot.get("id"));
- 直接在multiselect中加入join失败,因缺少聚合函数:
query.multiselect(entryRoot.get("id"), entryRoot.get("name"), entryRoot.join("groups", JoinType.LEFT));
- 尝试用
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
相关产品推荐
相关产品推荐

