为何TYPE_SCROLL_INSENSITIVE的ResultSet仍反映外部数据变更?
问题原因分析
- 代码中的ResultSet类型与描述不符:你提到使用了
TYPE_SCROLL_INSENSITIVE,但实际代码里写的是ResultSet.TYPE_SCROLL_SENSITIVE——这个类型本身就是设计用来感知外部数据变更的。 refreshRow()的强制刷新作用:不管你用哪种ResultSet类型,调用refreshRow()都会直接从数据库重新拉取当前行的最新数据,完全绕过INSENSITIVE类型的客户端缓存机制。- 事务自动提交的影响:Connection默认开启自动提交,第一次
executeQuery()执行后事务就会提交。sleep后的操作会在新事务中执行,此时外部提交的变更已经对新事务可见,再加上refreshRow()的调用,自然会拿到更新后的值。
如何用TYPE_SCROLL_SENSITIVE显示更新后的数据
你当前的代码已经实现了这个效果,只需注意两个细节:
- 确保Statement创建时明确指定
ResultSet.TYPE_SCROLL_SENSITIVE和ResultSet.CONCUR_READ_ONLY(你已经做到了)。 - MySQL的Connector/J驱动不会自动更新ResultSet内容,必须主动调用
refreshRow()触发单条数据刷新,或者重新执行executeQuery()获取全新的ResultSet来批量更新数据。
如何实现预期行为(外部变更不反映到ResultSet)
要让ResultSet始终保留初始查询的数据,不受外部变更影响,需要做以下调整:
- 修正ResultSet类型为
TYPE_SCROLL_INSENSITIVE:Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); - 移除
refreshRow()调用:这个方法会强制从数据库拉取最新数据,直接删除这行代码即可。 - 控制事务范围:关闭自动提交,让整个查询、等待、再次读取的操作处于同一个事务中,利用MySQL默认的
REPEATABLE READ隔离级别保证事务内的数据一致性:connection.setAutoCommit(false); // 关闭自动提交 Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); ResultSet resultSet = statement.executeQuery("SELECT * FROM table1"); // 第一次读取数据 if (resultSet.next()) { System.out.print(resultSet.getInt("id") + " "); System.out.print(resultSet.getString("name") + " "); System.out.print(resultSet.getString("value")); System.out.println(); } // 模拟外部修改延迟 Thread.sleep(10 * 1000); // 直接回到结果集开头重新读取,无需刷新 resultSet.beforeFirst(); // 第二次读取数据 if (resultSet.next()) { System.out.print(resultSet.getInt("id") + " "); System.out.print(resultSet.getString("name") + " "); System.out.print(resultSet.getString("value")); System.out.println(); } connection.commit(); // 最后提交事务 - 驱动特性验证:MySQL的Connector/J对
TYPE_SCROLL_INSENSITIVE的实现是将结果集缓存到客户端内存中,只要不主动触发刷新,就会一直保留初始查询的数据。
内容的提问来源于stack exchange,提问作者Daniel Prince
相关产品推荐
相关产品推荐

