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

如何在JPA Criteria查询中实现分组并拼接字段值?

解决JPA Criteria实现分组拼接字段的问题

看起来你想要通过JPA Criteria实现按incomeChannelCode和logicalUnitCode分组,将对应的logicalUnitIdent用逗号拼接的效果,之前尝试group_concat没生效大概率是因为缺少分组配置或者数据库方言的问题,下面给你两种可行的方案:

方案一:使用数据库聚合函数(以MySQL为例)

MySQL的GROUP_CONCAT函数确实可以实现字段拼接,但需要注意两个关键点:

  • 必须对分组字段添加GROUP BY
  • 确保Hibernate配置了正确的MySQL方言(比如org.hibernate.dialect.MySQL8Dialect)

修改你的Criteria查询代码如下:

public List<IncomeChannelCategoryMap> allIncomeChannels(final List<String> list) {
    final CriteriaQuery<IncomeChannelCategoryMap> criteriaQuery = builder.createQuery(IncomeChannelCategoryMap.class);
    final Root<IncomeChannelMapEntity> root = criteriaQuery.from(IncomeChannelMapEntity.class);
    
    // 定义要分组的字段
    Expression<String> incomeChannelCode = root.get(IncomeChannelMapEntity_.incomeChannel).get(IncomeChannelEntity_.code);
    Expression<String> logicalUnitCode = root.get(IncomeChannelMapEntity_.logicalUnitCode);
    
    // 使用GROUP_CONCAT拼接logicalUnitIdent
    Expression<String> concatIdent = builder.function(
        "group_concat", 
        String.class, 
        root.get(IncomeChannelMapEntity_.logicalUnitIdent)
    );
    
    // 构造查询选择项
    criteriaQuery.multiselect(
        incomeChannelCode,
        logicalUnitCode,
        concatIdent,
        root.get(IncomeChannelMapEntity_.keyword) // 注意:如果keyword不是分组字段,可能需要用聚合函数处理,比如取第一个
    );
    
    // 添加WHERE条件
    Predicate codePredicate = incomeChannelCode.in(list);
    criteriaQuery.where(codePredicate);
    
    // 添加GROUP BY
    criteriaQuery.groupBy(incomeChannelCode, logicalUnitCode);
    
    return entityManager.createQuery(criteriaQuery).getResultList();
}

注意:如果你的keyword字段在同一分组下有不同值,需要用聚合函数(比如builder.max()、builder.first())处理,否则数据库会报错。如果keyword和分组字段是一一对应的,直接加入group by即可。

如果是其他数据库,替换对应的聚合函数:

  • PostgreSQL:使用string_agg(root.get(...), ','),对应函数名"string_agg"
  • Oracle:使用listagg(root.get(...), ',') within group (order by root.get(...)),需要调整function的参数

方案二:内存中分组拼接(兼容性更好)

如果不想依赖数据库特定函数,或者数据库不支持类似的聚合函数,可以先查询出所有原始数据,再用Java代码在内存中分组拼接:

public List<IncomeChannelCategoryMap> allIncomeChannels(final List<String> list) {
    // 先查询所有符合条件的原始数据
    final CriteriaQuery<IncomeChannelCategoryMap> criteriaQuery = builder.createQuery(IncomeChannelCategoryMap.class);
    final Root<IncomeChannelMapEntity> root = criteriaQuery.from(IncomeChannelMapEntity.class);
    final List<Selection<?>> selections = new ArrayList<>();
    selections.add(root.get(IncomeChannelMapEntity_.incomeChannel).get(IncomeChannelEntity_.code));
    selections.add(root.get(IncomeChannelMapEntity_.logicalUnitCode));
    selections.add(root.get(IncomeChannelMapEntity_.logicalUnitIdent));
    selections.add(root.get(IncomeChannelMapEntity_.keyword));
    criteriaQuery.multiselect(selections);
    Predicate codePredicate = root.get(IncomeChannelMapEntity_.incomeChannel).get(IncomeChannelEntity_.code).in(list);
    criteriaQuery.where(codePredicate);
    List<IncomeChannelCategoryMap> rawList = entityManager.createQuery(criteriaQuery).getResultList();
    
    // 内存中分组拼接
    return rawList.stream()
        .collect(Collectors.groupingBy(
            item -> Arrays.asList(item.getIncomeChannelCode(), item.getLogicalUnitCode()),
            Collectors.collectingAndThen(
                Collectors.toList(),
                group -> {
                    IncomeChannelCategoryMap merged = new IncomeChannelCategoryMap();
                    merged.setIncomeChannelCode(group.get(0).getIncomeChannelCode());
                    merged.setLogicalUnitCode(group.get(0).getLogicalUnitCode());
                    merged.setKeyword(group.get(0).getKeyword()); // 同样,这里如果keyword有不同值需要处理
                    merged.setLogicalUnitIdent(group.stream()
                        .map(IncomeChannelCategoryMap::getLogicalUnitIdent)
                        .collect(Collectors.joining(",")));
                    return merged;
                }
            )
        ))
        .values()
        .stream()
        .collect(Collectors.toList());
}

这种方案的优点是不依赖数据库特性,兼容性强,缺点是如果数据量很大,会占用较多内存,适合数据量不大的场景。

为什么之前的GROUP_CONCAT没生效?

大概率是因为你没有添加GROUP BY语句,数据库不知道按照哪些字段分组拼接,所以返回的还是原始的多行数据。加上分组后,聚合函数才能正确工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:23:43