使用CriteriaAPI关联查询group_concat时触发NullPointerException问题
解决JPA CriteriaAPI调用group_concat时的NullPointerException问题
问题分析
触发java.lang.NullPointerException的核心原因有两点:
- GROUP BY子句不符合SQL规范:SELECT中的非聚合列(
registryGroup.id)未加入GROUP BY,导致Hibernate解析查询元数据时出现异常。 - group_concat参数传递错误:手动拼接的函数参数不符合Hibernate的参数解析逻辑,导致Types数组出现null元素,进而触发
NameGenerator.generateColumnNames方法的空指针异常。
解决方案
1. 修正GROUP BY子句
将SELECT中所有非聚合列加入GROUP BY,符合SQL语法要求:
c.groupBy(registryGroup.get(RegistryGroupEntity_.id), registryGroup.get(RegistryGroupEntity_.name));
2. 正确调用group_concat聚合函数
推荐使用Hibernate原生支持的GroupConcatFunction构造规范的聚合查询,避免手动拼接参数的问题:
import org.hibernate.query.criteria.internal.function.GroupConcatFunction; // 构造GROUP_CONCAT表达式,指定聚合列、去重规则、排序逻辑和分隔符 GroupConcatFunction serviceNamesConcat = new GroupConcatFunction( serviceJoin.get(ServiceEntity_.name), true, // 启用DISTINCT去重,不需要则设为false serviceJoin.get(ServiceEntity_.name), "DESC", // 排序方向 ", " // 分隔符 ); // 更新multiselect部分,注意别名不要与现有字段冲突 c.multiselect( registryGroup.get(RegistryGroupEntity_.id), registryGroup.get(RegistryGroupEntity_.name), serviceNamesConcat.alias("serviceNames") );
替代方案(不依赖Hibernate特定类)
如果不想引入Hibernate内部类,可以通过原生SQL片段构造正确的group_concat调用(确保参数为实体属性,避免SQL注入风险):
Expression<String> serviceNamesConcat = cb.function( "group_concat", String.class, cb.concat( "DISTINCT ", serviceJoin.get(ServiceEntity_.name), " ORDER BY ", serviceJoin.get(ServiceEntity_.name), " DESC SEPARATOR ', '" ) );
调整后的完整代码示例
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<RegistryGroupRow> c = cb.createQuery(RegistryGroupRow.class); Root<RegistryGroupEntity> registryGroup = c.from(RegistryGroupEntity.class); Join<RegistryGroupEntity, ServiceEntity> serviceJoin = registryGroup.join(RegistryGroupEntity_.services, JoinType.INNER); // 构造GROUP_CONCAT表达式 GroupConcatFunction serviceNamesConcat = new GroupConcatFunction( serviceJoin.get(ServiceEntity_.name), true, serviceJoin.get(ServiceEntity_.name), "DESC", ", " ); c.multiselect( registryGroup.get(RegistryGroupEntity_.id), registryGroup.get(RegistryGroupEntity_.name), serviceNamesConcat.alias("serviceNames") ); c.groupBy(registryGroup.get(RegistryGroupEntity_.id), registryGroup.get(RegistryGroupEntity_.name)); c.orderBy(cb.desc(registryGroup.get(RegistryGroupEntity_.id)));
内容的提问来源于stack exchange,提问作者Eljah
相关产品推荐
相关产品推荐

