CachedRowSet是JDK自带的离线RowSet实现,支持断连读取、内存操作与按需回写;通过RowSetProvider创建,需显式指定主键、设为可读写,修改后调用acceptChanges()触发乐观锁同步。

Java JDBC 中用 RowSet 实现离线数据缓存与操作,核心是选用 CachedRowSet —— 它是 JDK 自带、无需额外依赖、真正支持“断连读取→内存操作→按需回写”的标准实现。关键不在于接口定义,而在于它把整张结果集复制进内存后主动关闭 Connection 和 ResultSet,之后所有遍历、修改、定位都完全脱离数据库。
创建并填充 CachedRowSet 的两种可靠方式
推荐使用标准工厂方式(JDBC 4.1+):
- 调用
RowSetProvider.newFactory().createCachedRowSet()获取实例,确保兼容性和可维护性 - 设置连接参数:
setUrl()、setUsername()、setPassword();再用setCommand("SELECT id, name FROM user")指定语句(注意:避免SELECT *,必须显式包含主键列) - 执行
execute()—— 此时才建立连接、执行查询、加载全部数据到内存、自动关闭连接,后续全程离线
也可用传统方式(兼容老版本):
- 直接实例化
new com.sun.rowset.CachedRowSetImpl() - 先建立数据库连接,执行查询得到
ResultSet rs - 调用
crs.populate(rs)填充;随后立即关闭rs和Connection - 若原始表名未知,需手动调用
crs.setTableName("user"),否则更新/删除可能失败
在内存中安全地增删改查
所有操作仅影响本地缓存,不触发任何数据库交互,适合表单编辑、批量校验等场景:
立即学习“Java免费学习笔记(深入)”;
- 必须提前调用
crs.setReadOnly(false),否则任何修改都会抛SQLException - 定位行:
crs.absolute(3)跳转第 3 行,或crs.beforeFirst(); crs.next()逐行遍历 - 更新字段:
crs.updateString("name", "张三")→ 必须紧跟crs.updateRow()才生效(仅内存) - 插入新行:
crs.moveToInsertRow()→crs.updateInt("id", 100)→crs.updateString("name", "李四")→crs.insertRow() - 删除行:
crs.deleteRow()是逻辑删除(行仍保留在缓存中),直到调用acceptChanges()才真正从缓存移除
将修改同步回数据库的注意事项
acceptChanges() 是唯一触发数据库写入的操作,但它不是简单“提交”,而是由底层 SyncProvider(默认为 RIOptimisticProvider)协调完成:
- 它会检查主键是否匹配,确认目标行未被其他事务修改(乐观锁机制),若发现冲突则抛异常,不会静默覆盖
- 执行前请确保已设好主键列:
crs.setKeyColumns(new int[]{1})(假设第 1 列是主键) - 若需自定义冲突策略(如强制覆盖、合并字段、记录差异),需实现并注册自己的
RowSetWriter - 该操作会临时重建数据库连接,执行 SQL UPDATE/INSERT/DELETE,完成后再次断开
适用场景与风险提醒
CachedRowSet 适合数据量适中、需要客户端自由编辑、网络不稳定或需跨层传输(如 Web 层传给移动端)的场景。但要注意:
- 整表加载,内存占用随数据量线性增长,不适合超大数据集(如百万级结果)
- 无内置事务隔离,多线程并发修改同一
CachedRowSet实例需自行加锁 - 不支持实时数据库变更通知,缓存内容不会自动刷新,需业务层控制重载时机
- 若用于 Web 传输,可考虑其子类
WebRowSet,它支持序列化为 XML,便于前后端解耦


















