在Oracle中使用Hibernate的setFirstResult和setMaxResults时返回两列而非一列
我之前也碰到过一模一样的问题!大概率是Hibernate处理原生SQL分页时的默认行为搞的鬼——它会自动添加用于分页控制的额外列(比如Oracle的rownum),导致结果集里既有你要查的目标列,又有这个分页用的列,最终返回的是Object[]类型的列表,每个数组包含两个元素,看起来就像是返回了两列。
下面是具体的排查和解决办法:
1. 先确认实际执行的SQL
你已经开了showSQL=true,先看看Hibernate打印出来的实际执行SQL。如果打印的SQL里能看到类似rownum的额外列(比如Oracle的分页SQL会嵌套子查询并添加rownum),那就能实锤是分页逻辑导致的问题。
举个例子,Oracle中Hibernate生成的分页SQL可能长这样:
select * from ( select row_.*, rownum rownum_ from ( select your_target_column from your_table ) row_ where rownum <= ? ) where rownum_ > ?
这里的select *会把原列和rownum_都查出来,自然就返回两列了。
2. 最优解决方案:明确指定返回列
通过addScalar()方法明确告诉Hibernate你要查询的列和对应的数据类型,这样Hibernate在生成分页SQL时就只会选择你指定的列,不会用*把分页控制列也包含进来。
修改后的代码示例:
// 假设你查的是字符串类型的列,列名为your_target_column SQLQuery query = session.createSQLQuery("select your_target_column from your_table"); // 明确指定列名和数据类型,这里用StandardBasicTypes对应你列的类型 query.addScalar("your_target_column", StandardBasicTypes.STRING); int batchSize = 50; for (int i = 0; ; i++) { query.setFirstResult(batchSize * i); query.setMaxResults(batchSize); // 现在list()返回的是单个对象的列表,不再是Object数组 List<String> resultSet = query.list(); if (resultSet.isEmpty()) break; // 处理结果集,直接遍历单个元素就行 for (String value : resultSet) { // 你的业务逻辑 } }
这样Hibernate生成的分页SQL会自动调整为只选择你指定的列,额外的分页控制列不会被返回给结果集。
3. 临时应急方案:忽略额外列
如果暂时不想改查询配置,也可以在处理结果的时候只取数组的第一个元素(也就是你要的目标列):
List<Object[]> resultSet = query.list(); if(resultSet.isEmpty()) break; for (Object[] row : resultSet) { // 直接取第一列的值,无视分页用的第二列 Object targetValue = row[0]; // 处理targetValue }
这种方式简单粗暴,但不够优雅,还是推荐用第一种方案从根源解决问题。
4. 最后排查:确认原始SQL是否真的只查单列
别漏了最基础的检查——你的原始SQL是不是真的只查询了一列?比如有没有不小心写成select col1, col2 ...或者select *(如果表刚好有两列的话),这种低级错误也可能导致返回多列哦。
内容的提问来源于stack exchange,提问作者focus

