ResultSet.next()必须先调用才能取值,否则抛SQLException;字段名与列名不匹配时应统一用SQL别名或getColumnLabel;ResultSet需在遍历中关闭,避免NPE;性能优化需缓存字段映射、使用类型明确方法并设setAccessible(true)。

ResultSet.next() 必须在取值前调用,否则 SQLException: Before start of result set
很多新手一拿到 ResultSet 就直接 rs.getString("id"),结果立刻报错。JDBC 规定游标初始位置在第一行之前,不调用 next() 就无法读取任何数据。
正确做法是用 while (rs.next()) 包裹取值逻辑,而不是 if (rs.next())(除非你确定只有一条)。
-
rs.next()返回boolean,成功移动到下一行才返回true;失败(如已到末尾)返回false - 每调一次
next(),游标前进一行,不可回退(除非用rs.beforeFirst(),但多数驱动不支持可滚动结果集) - 如果 SQL 可能返回空结果,
while循环天然安全;用if容易漏掉多行场景
用反射自动封装时,字段名和列名不匹配怎么办
常见错误是直接用 rs.getObject(fieldName),但 fieldName 是 Java 对象的属性名(如 userName),而数据库列名可能是 user_name 或 USER_NAME —— 两者默认不等价。
别指望 JDBC 驱动自动做驼峰转换,它只认 SQL 返回的原始列名(rs.getMetaData().getColumnName(i))。
立即学习“Java免费学习笔记(深入)”;
- 最稳妥:统一用列别名,SQL 里写
SELECT user_name AS userName FROM users - 次选:用
rs.getMetaData().getColumnLabel(i)替代getColumnName(i),它返回AS后的别名(若没写别名,则同列名) - 避免用
rs.findColumn("userName")查列索引——它只按原始列名或别名匹配,不支持自动映射规则
ResultSet 关闭时机不对,导致对象字段全为 null
典型现象:封装完 ListResultSet、Statement、Connection 被提前关闭,底层数据流已断。
JDBC 的 ResultSet 是懒加载的,很多驱动(如 MySQL Connector/J)默认不把整张表读进内存,而是边遍历边从 socket 拉数据。
- 必须在
while (rs.next())循环内部完成对象构建,不能把未消费的ResultSet传给其他方法延迟处理 - 不要在 finally 块里关
rs后再 return list——循环结束前就关了,后面取值必然失败 - 推荐用 try-with-resources,但要确保
rs、stmt、conn的生命周期覆盖完整遍历过程
性能陷阱:频繁调用 rs.getObject() + 反射 setAccessible(true)
每次 rs.getObject("col") 都有类型检查开销;每次 Field.set() 若未设 setAccessible(true),会触发安全检查,慢 3–5 倍。
这不是理论问题,在万级记录封装时,差异明显。
- 优先用类型明确的方法:
rs.getLong("id")、rs.getString("name"),比getObject快且类型安全 - 反射设置字段前,务必
field.setAccessible(true),且只设一次(缓存Field对象) - 别在循环里反复
Class.getDeclaredField(),字段查找本身就有开销
真正难的不是怎么写反射,是怎么让反射只做一次准备、多次复用。绕不开的点就在这里:字段映射关系得缓存,列名到索引的映射也得缓存,否则每行都在重复解析元数据。










