Hibernate Search 6升级后Scrollable投影查询结果返回格式异常
Hibernate Search 6 滚动查询投影结果类型异常修复
问题现象
自Hibernate Search 5.11升级至Hibernate Search 6后,使用Scrollable滚动结果集时出现类型不匹配问题:
- 从
SearchScroll对象获取分块命中结果时,每个命中项均为Arrays.ArrayList类型 - 预期返回元素类型为
Long的结果集,实际返回的列表中,每个命中项都是包裹Long值的Arrays.ArrayList实例 - 搜索返回的实际数据内容正确,仅数据封装格式不符合预期
- 移除投影配置后,返回结果为预期的目标对象类型集合,确认问题由投影配置触发
- 已尝试手动遍历结果转换嵌套的
Arrays.ArrayList为Long类型,未找到可行实现;尝试声明SearchScroll<Long>泛型未解决问题
相关代码
Hibernate Search 6 异常代码
SearchScroll scroll = searchSession .search(scope) .select(projectionArray) .where(searchPredicate) .sort(getSort(resultType)) .scroll(20);
添加泛型声明后的无效调整代码:
SearchScroll<Long> scroll = searchSession .search(scope) .select(projectionArray) .where(searchPredicate) .sort(getSort(resultType)) .scroll(20);
Hibernate Search 5 正常运行旧代码
FullTextQuery fullTextQuery = fullTextSession .createFullTextQuery(query, resultType) .setSort(getSort(resultType)); fullTextQuery.setProjection(fields); ScrollableResults scrollableResults = fullTextQuery.scroll();
根本原因
Hibernate Search 6调整了投影的类型返回逻辑,和5版本的自动拆包行为存在差异:
- 当
select()方法传入投影数组时,无论数组中包含1个还是多个投影字段,框架都会将每个命中结果封装为List/Object[]结构,对应每个投影字段的值 - Hibernate Search 5会在单字段投影时自动拆包返回单个值,6版本不再做该隐式转换,严格按照传入的投影结构返回结果
- 代码中传入的
projectionArray是数组类型,因此每个命中项被封装为列表,才会出现嵌套的Arrays.ArrayList情况,和泛型声明无关。
解决方案
根据实际投影需求选择对应写法即可:
- 单字段投影(仅需返回Long类型ID等单个字段)
不要传入数组类型的投影参数,直接传入单个投影定义,框架会直接返回对应单值类型:
// 示例:仅投影ID字段,直接传入单个投影,不要包装为数组 SearchScroll<Long> scroll = searchSession .search(scope) .select(f -> f.id(Long.class)) // 替换为实际的单个投影定义 .where(searchPredicate) .sort(getSort(resultType)) .scroll(20); // 遍历方式 try (scroll) { for (SearchScrollResult<Long> chunk = scroll.next(); chunk.hasHits(); chunk = scroll.next()) { for (Long id : chunk.hits()) { // 直接使用Long类型id,无嵌套列表 } entityManager.flush(); entityManager.clear(); } }
- 多字段投影(需同时返回多个字段)
正确声明泛型为列表类型,遍历结果时按投影字段的顺序从列表中取值即可:
// 多投影场景下每个命中项为存储各字段值的List SearchScroll<List<?>> scroll = searchSession .search(scope) .select(projectionArray) .where(searchPredicate) .sort(getSort(resultType)) .scroll(20); // 遍历方式 try (scroll) { for (SearchScrollResult<List<?>> chunk = scroll.next(); chunk.hasHits(); chunk = scroll.next()) { for (List<?> hit : chunk.hits()) { // 按投影字段的定义顺序取值,例如第一个投影为ID则取索引0 Long id = (Long) hit.get(0); // 其余字段按对应索引依次获取 } entityManager.flush(); entityManager.clear(); } }
注意:Hibernate Search 6中
SearchScroll实现了AutoCloseable接口,必须通过try-with-resources语法手动关闭,否则会出现索引连接泄漏问题,这是和5版本的重要差异。
内容的提问来源于stack exchange,提问作者Ashley Callaghan
相关产品推荐
相关产品推荐

