锁定老帖子 主题:【原创】JDBC ResultSet分析
精华帖 (5) :: 良好帖 (14) :: 新手帖 (0) :: 隐藏帖 (0)
|
|||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
作者 | 正文 | ||||||||||||
发表时间:2009-12-30
最后修改:2010-01-26
JDBC1.0 、JDBC2.0 、JDBC3.0 中分别用以下方法创建Statement 。 JDBC1.0 : createStatement() JDBC2.0 : createStatement(resultSetType, resultSetConcurrency) JDBC3.0 : createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)
下面依次分析resultSetType 、resultSetConcurrency 、resultSetHoldability 这几个参数的含义。
一 ResultSetType
resultSetType 的可选值有: ResultSet.TYPE_FORWARD_ONLY 、ResultSet.TYPE_SCROLL_INSENSITIVE 、ResultSet.TYPE_SCROLL_SENSITIVE 。
1 :ResultSet.TYPE_FORWARD_ONLY 默认的cursor 类型,仅仅支持结果集forward ,不支持backforward ,random ,last ,first 等操作。
2 :ResultSet.TYPE_SCROLL_INSENSITIVE 支持结果集backforward ,random ,last ,first 等操作,对其它session 对数据库中数据做出的更改是不敏感的。 实现方法:从数据库取出数据后,会把全部数据缓存到cache 中,对结果集的后续操作,是操作的cache 中的数据,数据库中记录发生变化后,不影响cache 中的数据,所以ResultSet 对结果集中的数据是INSENSITIVE 的。
3 :ResultSet.TYPE_SCROLL_SENSITIVE 支持结果集backforward ,random ,last ,first 等操作,对其它session 对数据库中数据做出的更改是敏感的,即其他session 修改了数据库中的数据,会反应到本结果集中。
实现方法:从数据库取出数据后,不是把全部数据缓存到cache 中,而是把每条数据的rowid 缓存到cache 中,对结果集后续操作时,是根据rowid 再去数据库中取数据。所以数据库中记录发生变化后,通过ResultSet 取出的记录是最新的,即ResultSet 是SENSITIVE 的。 但insert 和delete 操作不会影响到ResultSet ,因为insert 数据的rowid 不在ResultSet 取出的rowid 中,所以insert 的数据对ResultSet 是不可见的,而delete 数据的rowid 依旧在ResultSet 中,所以ResultSet 仍可以取出被删除的记录( 因为一般数据库的删除是标记删除,不是真正在数据库文件中删除 )。
做个试验,验证一下SENSITIVE 特性。数据库为oracle10g ,驱动为ojdbc14.jar 。 test 表中数据如下:
程序如下: public static void testResultSetSensitive(Connection conn) throws Exception{ String sql = "SELECT c1,c2,c3 FROM test"; try { Statement stmt = conn .createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { System.out.println("[行号:" + rs.getRow() + "]\t" + rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3)); Thread.sleep(20000); } rs.close(); stmt.close(); } catch (SQLException e) { e.printStackTrace(); } finally { try { conn.close(); } catch (Exception e) { } } }
定义ResultSet 为 ResultSet. TYPE_SCROLL_SENSITIVE 类型,首先执行 sql 访问数据库,然后执行 rs.next() 移动游标取数据。在循环里面加上 Thread.sleep (20000) 的目的是为了我们有时间在后台把数据库里的数据改了。比如当在循环里打印出第一行的数据后,我们在后台,把第三行数据的 c3 列改成 ”3uuu” 。如果 ResultSet 真的是敏感的话,那应该取出 ”3uuu” ,而不是原始的“ 3c 3 ”。但最终的结果却是如下:
[ 行号: 1] 1c1 1c2 1c3 [ 行号: 2] 2c1 2c2 2c3 [ 行号: 3] 3c1 3c2 3c3
数据没变呀,ResultSet 不敏感啊!于是去查阅资料,找了n 久,还是在英文文档上找到了答案。原来是fetchsize 的问题。调用ResultSet 的next 方法取数据时,并不是每调用一次方法就去数据库里查一次,而是有个fetchSize, 一次取fetchSize 条数据。Oracle 默认的fetchsize 等于10 ,所以上面的代码在第一次调用rs.next() 时,就已经把3 条数据都取出来了,所以才会有上面的结果。
第二次实验,在ResultSet rs = stmt.executeQuery(sql); 前面加上 stmt.setFetchSize(1); 将fetchSize 设置为1 。然后重新第一次实验的步骤,发现最 终结果为: [ 行号: 1] 1c1 1c2 1c3 [ 行号: 2] 2c1 2c2 2c3 [ 行号: 3] 3c1 3c2 3uuu 原因就是 fetchsize 设置为 1 时,每次 next 取数时都会重新用 rowid 取数据库里取数据,当然取到的是最新的数据了。
二 ResultSetConcurrency
ResultSetConcurrency的可选值有2个:
三 ResultSetHoldability ResultSetHoldability 的可选值有2 个 : HOLD_CURSORS_OVER_COMMIT:
在事务commit
或rollback
后,ResultSet
仍然可用。
需要注意的地方: 1 :Oracle 只支持HOLD_CURSORS_OVER_COMMIT 。 2 :当Statement 执行下一个查询,生成第二个ResultSet 时,第一个ResultSet 会被关闭,这和是否支持支持HOLD_CURSORS_OVER_COMMIT 无关。
四 验证数据库是否支持ResultSet的各种特性
不同的数据库版本及 JDBC 驱动版本,对 ResultSet 的各种高级特性的支持是不一样的,我们可以通过以下方法,来验证具体的数据库及 JDBC 驱动,是否支持 ResultSet 的各种特性。
DatabaseMetaData dbMeta = conn.getMetaData(); 然后调用 DatabaseMetaData 对象的以下方法: boolean supportsResultSetType(int resultSetType); boolean supportsResultSetConcurrency(int type, int concurrency); boolean supportsResultSetHoldability(int holdability);
参考的2 篇英文文档: http://cs.felk.cvut.cz/10gr2/java.102/b14355/jdbcvers.htm (JDBC Standards Support ) http://download.oracle.com/docs/cd/B10501_01/java.920/a96654/resltset.htm#1023642 (Oracle9i JDBC Developer's Guide and Reference Release 2 (9.2))
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2009-12-31
实际的各种jdbc实现,并非按照标准来的,特别是一些边边角角的参数配置等。
以前看derby10.x的代码,看到很多选项里写着,暂不支持之类的东西。 |
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2009-12-31
JDBC的实现根据厂商的不同而不同,以此概彼。如MS sql2000和2005驱动就很不同,还不能同时用
|
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2009-12-31
同意楼上。
不过也学习咯。 |
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2009-12-31
顶!
我个人感觉,最难掌握的是什么时候使用这些特性。 能否给出几个真实的使用场景。 |
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2010-01-04
感谢楼主的试验。
我下两个结论,不知道对不对。 1)进行查询时,oracle会根据查询的sql把符合条件的数据进行缓存。至于INSENSITIVE还是SENSITIVE,这是要告诉数据库如何作缓存,INSENSITIVE是把全部数据缓存到cache,SENSITIVE是把每条记录的rowid缓存到cache中。 2)resultSetType是SCROLL还是默认的FORWARD_ONLY,都是jdbc自己的行为,不会影响oracle内部的处理过程。 |
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2010-01-04
楼主的3 :ResultSet.TYPE_SCROLL_SENSITIVE例子并不能很好地证明结论。
原因如下: 1.例子环境并不是并发,你使用Sleep方法不能产生同步效应 2.例子中,数据的一致性那是事务的范畴。 3.例子中,你一条条地取即使是INSENSITIVE,都可能会是同步数据。 4.JDBC规范中,并没有规定非分布式Connection的实现机制,也就说Connection是使用的“拉”还是“推”的模式来同步数据,无论是“拉”还是“推”,使用Sleep都是不合适的。 |
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2010-01-04
mercyblitz 写道 楼主的3 :ResultSet.TYPE_SCROLL_SENSITIVE例子并不能很好地证明结论。 原因如下: 1.例子环境并不是并发,你使用Sleep方法不能产生同步效应 2.例子中,数据的一致性那是事务的范畴。 3.例子中,你一条条地取即使是INSENSITIVE,都可能会是同步数据。 4.JDBC规范中,并没有规定非分布式Connection的实现机制,也就说Connection是使用的“拉”还是“推”的模式来同步数据,无论是“拉”还是“推”,使用Sleep都是不合适的。 mercyblitz,你好!你表达的意思,我不是很明白。我用sleep的原因是让程序生成ResultSet后,暂停取数,然后启动另一会话或事务去改掉数据库中的数据,然后检查原程序生成的ResultSet中取出的数据是否是新的。这和你说的“同步”好像没关系吧? |
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2010-01-04
关于resultSetType, resultSetConcurrency, resultSetHoldability 这3个参数的实际使用,欢迎大家群策群力,积极回帖,共同提高嘛!
|
|||||||||||||
返回顶楼 | |||||||||||||
发表时间:2010-01-05
fuwang 写道 感谢楼主的试验。 我下两个结论,不知道对不对。 1)进行查询时,oracle会根据查询的sql把符合条件的数据进行缓存。至于INSENSITIVE还是SENSITIVE,这是要告诉数据库如何作缓存,INSENSITIVE是把全部数据缓存到cache,SENSITIVE是把每条记录的rowid缓存到cache中。 2)resultSetType是SCROLL还是默认的FORWARD_ONLY,都是jdbc自己的行为,不会影响oracle内部的处理过程。 resultSetType是FORWARD_ONLY时,是不缓存数据的。经测试,ResultSet.TYPE_SCROLL_INSENSITIVE 、ResultSet.TYPE_SCROLL_SENSITIVE下,查询大批量数据会导致溢出,而FORWARD_ONLY不会。 |
|||||||||||||
返回顶楼 | |||||||||||||