Hibernate-JPA:如何向CriteriaBuilder查询传递含下划线的字符串?
解决CriteriaBuilder按LISTAGG生成字段排序的问题
核心问题原因
Hibernate对排序字段的字符串解析会把下划线当作分隔符,自动截断别名(比如col_12_0_会被识别为col),而自动生成的查询别名(如col_1_0_)本身带下划线,导致排序失效。另外PhysicalNamingStrategyStandardImpl负责的是实体与数据库列的命名映射,和查询时的动态别名无关,所以修改它没用。
可行解决方案
1. 自定义别名+手动绑定排序逻辑
不要依赖Hibernate自动生成的带下划线别名,给LISTAGG表达式指定不含下划线的自定义别名,然后在处理Pageable排序时,手动将排序字段映射到该表达式:
// 1. 构建LISTAGG表达式并指定自定义别名(避免下划线) Expression<String> aggregatedField = cb.function( "LISTAGG", String.class, root.get("yourEntityProperty"), // 要聚合的实体属性 cb.literal(", ") // 分隔符 ).alias("aggregated_field"); // 自定义别名,无下划线 // 2. 将该表达式加入查询的select列表 criteriaQuery.select(cb.construct(YourResultDto.class, root.get("id"), aggregatedField)); // 3. 处理Pageable的排序,替换为对应表达式的排序 Sort originalSort = pageable.getSort(); List<Order> criteriaOrders = new ArrayList<>(); for (Sort.Order order : originalSort) { if ("aggregated_field".equals(order.getProperty())) { // 直接引用LISTAGG表达式进行排序,跳过Hibernate的字符串解析 criteriaOrders.add(order.isAscending() ? cb.asc(aggregatedField) : cb.desc(aggregatedField)); } else { // 普通实体属性按原有逻辑排序 criteriaOrders.add(order.isAscending() ? cb.asc(root.get(order.getProperty())) : cb.desc(root.get(order.getProperty()))); } } criteriaQuery.orderBy(criteriaOrders);
2. 直接使用表达式排序(无需别名)
如果不需要在返回结果中暴露别名,可以直接跳过别名,在排序时直接引用LISTAGG的表达式对象:
Expression<String> listAggExpr = cb.function( "LISTAGG", String.class, root.get("yourEntityProperty"), cb.literal(", ") ); // 加入select列表 criteriaQuery.select(cb.construct(YourResultDto.class, root.get("id"), listAggExpr)); // 直接用表达式排序(可根据Pageable动态生成升/降序) criteriaQuery.orderBy(cb.desc(listAggExpr));
关键注意点
- 避免使用带下划线的自定义别名,防止Hibernate的排序解析逻辑截断字段名
- 不要依赖字符串形式的别名进行排序,直接引用表达式对象可以彻底绕开Hibernate的别名解析问题
内容的提问来源于stack exchange,提问作者Arturillo Smeriglia
相关产品推荐
相关产品推荐

