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

如何在JPA Query中通过GROUP BY生成公司ID对应设备名列表的Map结构

结论

标准JPQL本身没有原生支持直接返回聚合后嵌套列表结构的语法,但是根据你使用的JPA实现和版本,可以通过以下方案实现需求:


方案1:Hibernate 6+ 版本实现(最贴近你期望的写法)

Hibernate 6及以上版本内置了collect_list/collect_set聚合函数,可以直接将多行值聚合为列表,你可以直接返回你定义的CompanyDevices对象:

public interface SomeRepository extends JpaRepository<SomeEntity, Long> {
    
    @Query("SELECT " +
            "new 你实际的包路径.CompanyDevices(C.id, collect_list(D.deviceName)) " +
            "FROM Company C " +
            "JOIN C.employees E " +
            "JOIN E.devices D " +
            "WHERE C.id IN :companyIds " +
            "GROUP BY C.id"
    )
    List<CompanyDevices> findDevicesForCompanies(@Param("companyIds") List<Long> companyIds);
}

如果需要直接返回Map<Long, List<String>>结构,搭配@MapKey注解即可实现:

@Query("SELECT " +
        "C.id as key, collect_list(D.deviceName) as value " +
        "FROM Company C " +
        "JOIN C.employees E " +
        "JOIN E.devices D " +
        "WHERE C.id IN :companyIds " +
        "GROUP BY C.id"
)
@MapKey(name = "key")
Map<Long, List<String>> findDevicesForCompanies(@Param("companyIds") List<Long> companyIds);

方案2:低版本JPA实现使用原生SQL

如果你使用的JPA版本不支持collect_list函数,可以使用对应数据库的字符串聚合函数,先把设备名拼接为单个字符串,后续再拆分转换为列表,以下是MySQL为例的写法:

@Query(value = "SELECT c.id, GROUP_CONCAT(d.device_name SEPARATOR ',') as device_names " +
        "FROM company c " +
        "JOIN employee e ON c.id = e.company_id " +
        "JOIN devices d ON e.id = d.employee_id " +
        "WHERE c.id IN :companyIds " +
        "GROUP BY c.id", nativeQuery = true)
List<Object[]> findRawDeviceGroups(@Param("companyIds") List<Long> companyIds);

不同数据库的字符串聚合函数有差异:PostgreSQL用string_agg、Oracle用listagg,需要根据你使用的数据库调整语法


方案3:保留现有写法+Stream分组(通用性最强,性能开销可忽略)

你现有的写法已经是兼容性最好的实现,查询结果只需要一行Stream代码即可完成分组,适配所有JPA实现和数据库,额外性能开销极低:

// 调用现有Repository方法后直接分组
Map<Long, List<String>> companyDeviceMap = findDevicesForCompanies(companyIds).stream()
        .collect(Collectors.groupingBy(
                CompanyDevice::getCompanyId,
                Collectors.mapping(CompanyDevice::getDeviceName, Collectors.toList())
        ));

该方式的分组操作只是对已加载到内存的对象做引用重组,不会产生额外的大量对象开销,性能几乎和直接返回聚合结构一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 21:54:02