Spring MVC+Hibernate按Driver分组查询异常排查与实现咨询
咱们一步步拆解你的问题:你的HQL查询能成功执行,但在JSP页面显示分组后的Driver数据时抛出了NumberFormatException。这大概率是查询结果的结构和页面期望不匹配,或者实体映射/类型转换出了问题,下面详细分析:
先搞懂NumberFormatException的根源
这个异常本质是页面尝试把非数字类型的内容转成数字。比如你在JSP里写了${driver.id},但实际传给页面的对象根本不是Driver实体,而是一个Object数组,页面找不到getId()方法,底层会把数组当成字符串处理,转数字时直接炸锅。
你可能踩的常见坑:错误处理HQL查询结果
假设你写的HQL是这样的:
SELECT d.id, d.name FROM Driver d GROUP BY d.id, d.name
如果你的DAO层直接把查询结果强转成List<Driver>,那这就是核心错误!因为这种指定字段的HQL返回的不是Driver实体,而是Object数组(Object[])——数组的第一个元素是id,第二个是name。
当Controller把这个伪装成List<Driver>的List<Object[]>传到JSP,页面调用${driver.id}时,会尝试调用driver.getId(),但Object数组根本没有这个方法,最终触发类型转换异常,表现为NumberFormatException。
Hibernate Group By的正确用法
根据你的需求(获取Driver的id和name并分组),分两种场景处理:
场景1:返回完整Driver实体(当分组字段是主键)
如果你的分组是基于Driver的主键id(主键本身唯一,分组后和查询整个实体效果一致),可以直接查询实体:
FROM Driver d GROUP BY d.id
此时DAO层返回List<Driver>,Controller传到JSP后,正常用${driver.id}、${driver.name}即可,不会有类型问题。
场景2:返回指定字段(更高效,适合只需要部分数据的场景)
如果只需要id和name,或者要配合聚合函数(比如统计每个司机的订单数),推荐两种写法:
方式1:用Object数组接收结果
DAO层代码:
public List<Object[]> getDriverIdAndNameGrouped() { Session session = sessionFactory.getCurrentSession(); Query query = session.createQuery("SELECT d.id, d.name FROM Driver d GROUP BY d.id, d.name"); return query.list(); }
Controller把List<Object[]>传到JSP后,页面按数组索引取值:
<c:forEach items="${driverList}" var="driverArr"> <tr> <td>${driverArr[0]}</td> <!-- 对应id --> <td>${driverArr[1]}</td> <!-- 对应name --> </tr> </c:forEach>
方式2:用DTO构造函数映射(更优雅,避免数组索引混乱)
先创建一个DriverDTO类,专门用来接收查询结果:
public class DriverDTO { private Long id; private String name; // 必须和HQL里的字段顺序匹配的构造函数 public DriverDTO(Long id, String name) { this.id = id; this.name = name; } // 提供getter方法供EL表达式调用 public Long getId() { return id; } public String getName() { return name; } }
然后修改HQL,直接构造DTO:
SELECT new com.yourpackage.DriverDTO(d.id, d.name) FROM Driver d GROUP BY d.id, d.name
DAO层返回List<DriverDTO>,Controller传到JSP后,就可以像用实体一样调用属性:
<c:forEach items="${driverDTOList}" var="driver"> <tr> <td>${driver.id}</td> <td>${driver.name}</td> </tr> </c:forEach>
关键语法规则:Group By的约束
Hibernate的HQL遵循SQL的Group By规则:
- Select语句中的非聚合字段,必须全部出现在Group By子句中(如果主键在Group By里,因为主键唯一确定其他字段,可省略其他字段)
- 聚合函数(比如
count(d.id)、sum(d.orderCount))不需要出现在Group By里
可能存在的映射错误
- 实体类id字段类型不匹配:比如数据库里id是
INT/BIGINT,但实体类里id定义成String,或者反过来。当查询返回id后,JSP尝试转数字时就会抛出NumberFormatException。检查Driver实体类:
// 正确示例(数据库id为BIGINT时) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 不要用String,除非数据库是字符型主键
- DAO层类型转换错误:强行把
List<Object[]>转成List<Driver>,这会导致隐式的类型转换问题,在页面渲染时触发异常。 - JSP EL表达式错误:比如属性名大小写错误(比如把
name写成Name),或者用实体的属性访问方式去访问Object数组。
快速排查步骤
- 在DAO层打印查询结果:遍历返回的List,打印每个元素的类型和值,确认是Driver实体还是Object数组。
- 检查JSP里的EL表达式,确保和返回的对象结构匹配。
- 验证Driver实体类的id字段类型和数据库字段类型完全一致。
- 确认HQL的Group By语法正确,Select的非聚合字段都在Group By里(或为主键)。
内容的提问来源于stack exchange,提问作者Vikas Biradargoudar

