ResultSet简介
ResultSet是Java JDBC中表示数据库查询结果的数据表
学习目标
理解ResultSet的基本概念、类型和生命周期
什么是ResultSet?
ResultSet对象表示数据库查询的结果集,通常通过执行Statement对象的executeQuery()方法返回。
主要特点:
- 以表格形式组织数据
- 维护一个指向当前行的游标
- 提供多种方法获取不同类型的数据
- 支持向前和双向滚动(取决于类型)
ResultSet类型
TYPE_FORWARD_ONLY
TYPE_SCROLL_INSENSITIVE
TYPE_SCROLL_SENSITIVE
TYPE_SCROLL_SENSITIVE
+ HOLD_CURSORS_OVER_COMMIT
创建Statement时指定ResultSet类型:
打开状态
游标位于第一行之前
遍历中
使用next()方法移动游标
关闭状态
调用close()方法后
技术要点
ResultSet默认是TYPE_FORWARD_ONLY(只能向前移动)和CONCUR_READ_ONLY(只读)。要使用高级功能,需要在创建Statement时指定参数。
遍历结果集
使用ResultSet的游标控制方法遍历查询结果
学习目标
掌握ResultSet的游标控制方法和遍历技巧
基本遍历方法
注意:在访问数据前必须调用next()方法将游标移动到第一行
可滚动结果集遍历
next()
将游标从当前位置向前移动一行。如果新行有效则返回true,如果没有更多行则返回false。
previous()
将游标从当前位置向后移动一行。仅适用于可滚动结果集。
first() / last()
将游标移动到第一行/最后一行。仅适用于可滚动结果集。
absolute() / relative()
absolute(n)移动到第n行,relative(n)从当前位置移动n行。支持正数和负数参数。
性能建议
对于大型结果集,使用setFetchSize()方法优化性能。例如:stmt.setFetchSize(100) 表示每次从数据库获取100行数据。
获取数据
使用ResultSet的getXXX方法获取不同类型的数据
学习目标
掌握各种数据类型的获取方法和最佳实践
基本数据类型获取
注意:列索引从1开始,而不是0
处理空值
常用数据类型获取方法
getInt()
getLong()
getShort()
getByte()
getFloat()
getDouble()
getBigDecimal()
getString()
getNString()
getCharacterStream()
getDate()
getTime()
getTimestamp()
getBlob()
getClob()
getBinaryStream()
getBoolean()
getObject()
getArray()
最佳实践
优先使用列名而不是列索引获取数据,这样即使数据库表结构发生变化(列顺序改变),代码也不会出错。
使用元数据
通过ResultSetMetaData获取结果集的结构信息
学习目标
掌握ResultSetMetaData的使用方法和应用场景
获取元数据
元数据应用场景
ResultSetMetaData在以下场景非常有用:
- 动态生成报表
- 通用数据导出工具
- ORM框架实现
- 数据库管理工具
- 动态SQL查询处理
示例:动态处理任何查询结果
性能提示
获取ResultSetMetaData可能会有性能开销,特别是在大型结果集上。在性能敏感的场景中,应谨慎使用。
可滚动结果集
使用可滚动ResultSet实现灵活的数据访问
学习目标
掌握可滚动结果集的创建和使用方法
创建可滚动结果集
类型说明:
- TYPE_SCROLL_INSENSITIVE:可滚动,对数据库变化不敏感
- TYPE_SCROLL_SENSITIVE:可滚动,对数据库变化敏感
游标定位方法
注意:不是所有数据库都完全支持可滚动结果集,使用前请检查数据库支持情况
实践练习
- 实现一个方法,获取结果集的总行数
- 创建一个分页查询功能,显示第N页的数据
- 实现结果集的随机访问功能
- 比较TYPE_SCROLL_INSENSITIVE和TYPE_SCROLL_SENSITIVE的区别
可更新结果集
通过ResultSet直接更新数据库数据
学习目标
掌握可更新结果集的使用方法和限制
创建可更新结果集
注意:不是所有查询都能生成可更新结果集。查询必须包含主键且来自单个表。
更新数据
使用建议
可更新结果集适用于简单的单表操作。对于复杂更新,建议使用PreparedStatement,因为它提供更好的性能和安全性。
处理大数据集
优化ResultSet处理大型查询结果的性能
学习目标
掌握处理大型结果集的技术和最佳实践
使用Fetch Size优化
设置fetch size可以减少数据库网络往返次数,提高大型结果集的处理效率。
流式处理
注意:流式处理可以避免内存溢出,但需要保持数据库连接打开直到处理完成。
性能提示
处理超大型数据集时:
- 使用分页查询而不是一次性获取所有数据
- 只选择需要的列,避免SELECT *
- 使用流式处理减少内存占用
- 考虑在数据库端进行聚合处理
- 使用批处理操作减少数据库交互
常见错误与解决方案
ResultSet使用中的典型问题及解决方法
学习目标
识别并解决ResultSet使用中的常见问题
ResultSet is closed
错误信息: java.sql.SQLException: ResultSet is closed
原因:
- 在Statement关闭后访问ResultSet
- 在Connection关闭后访问ResultSet
- 显式调用了ResultSet.close()
解决方案:
- 确保在访问ResultSet时相关资源未关闭
- 使用try-with-resources管理资源生命周期
- 在关闭Statement/Connection前处理完ResultSet
Invalid cursor state
错误信息: java.sql.SQLException: Invalid cursor state
原因:
- 在调用next()之前访问数据
- 游标位于第一行之前或最后一行之后
- 尝试在空结果集上访问数据
解决方案:
- 在访问数据前调用next()方法
- 检查结果集是否为空:if (rs.next()) { ... }
- 使用isBeforeFirst()和isAfterLast()检查游标位置
Column not found
错误信息: java.sql.SQLException: Column 'X' not found
原因:
- 列名拼写错误
- 列名大小写不匹配
- 查询中不包含该列
解决方案:
- 检查列名拼写和大小写
- 使用ResultSetMetaData获取实际列名
- 使用列索引代替列名
Data type mismatch
错误信息: java.sql.SQLException: Data type mismatch
原因:
- 使用错误的getXXX方法获取数据
- 数据库列类型与Java类型不兼容
- 尝试将NULL值获取为基本类型
解决方案:
- 使用匹配的getXXX方法
- 使用getObject()获取值后再转换
- 使用包装类处理可能的NULL值
Operation not supported
错误信息: java.sql.SQLException: Operation not supported for this type of result set
原因:
- 尝试在只读结果集上更新数据
- 在TYPE_FORWARD_ONLY结果集上调用previous()
- 数据库驱动不支持该操作
解决方案:
- 创建Statement时指定正确的类型和并发模式
- 检查数据库驱动是否支持所需功能
- 使用PreparedStatement代替可更新结果集
Memory overflow
错误信息: java.lang.OutOfMemoryError: Java heap space
原因:
- 一次性加载超大型结果集
- 未设置fetch size导致加载所有数据
- 在内存中处理大量数据
解决方案:
- 使用setFetchSize()分批获取数据
- 使用流式处理结果集
- 增加JVM堆内存
- 优化查询只获取必要数据
调试技巧
调试ResultSet问题时:
- 打印ResultSetMetaData信息
- 检查游标位置(isBeforeFirst(), isAfterLast())
- 使用try-catch捕获并打印SQLException详细信息
- 启用JDBC日志记录
- 使用数据库工具验证查询结果