关于JDBC的ResultSet.TYPE_SCROLL_SENSITIVE无效的问题解析

感谢原文作者的剖析和总结

本文链接地址: 关于JDBC的ResultSet.TYPE_SCROLL_SENSITIVE无效的问题解析


关于JDBC的ResultSet.TYPE_SCROLL_SENSITIVE无效的问题解析。这两天看JDBC的这部分,我先开始用mysql数据库+mysql-connector-java-5.1.12试了大半天,但设了
ResultSet.TYPE_SCROLL_SENSITIVE怎么都无效,即打开了结果集,先用线程睡眠,然后更新数据库,然后再使用next方法将
游标置于被修改的位置,但从结果集读出的数据依然是未修改之前的值。后来用微软的sqlserver的jdbc测试,才看到了
ResultSet.TYPE_SCROLL_SENSITIVE的真正效果。

在网上查了半天,查到一篇有用的(其实是我都已经搞明白,又回头看这篇文章才发现了,原来它也有写,但之前看的时候只看了前半部分,最后那点没有看,其实我自己倒觉得,那部分其实反倒是我们最容易忽略的一个问题所在),这篇文章的网址是http://zhenkm0507.javaeye.com/blog/560109

还是先说一下关于ResultSet.TYPE_SCROLL_SENSITIVE和
ResultSet.TYPE_SCROLL_INSENSITIVE的差别,因为从java文档上关于这个常量的描述我觉得有点模糊,甚至在网上我还看
到了曲解(还是老外的一篇帖子,让我走了不少的弯路,害人不浅),我也做不来学术性的概念描述,但举个最直观的例子来说明它们两个间的差别,就是当得到
resultset后,当它调用next方法来将游标指到下一行之前,如果数据库里对应的下一行的数据发生了变化,当调用了next方法后,使用
getXXX取数据的时候,如果设置的是SENSITIVE,就会看到改变后的数据,如果设置的是INSENSITIVE就看不到更新后的结果。

这里要注意几点:对数据的改变只限于update已存在的数据,使用delete或insert删除或增加的数据都不会反应到resultset中,另外
要使resultset的游戏重指向到更改的那一行才能看到更新后的数据,我只试了next方法,至于使用absolute等其他方法是否可以,我没做测
试不敢打保票,但如果在第一次使用getXXX读取数据后,没有移动游标来重新指向这一行,就再调用getXXX方法是看不到数据库的更新的。

大概先说了一下它的基本的效果,下面就说重点,为什么设了ResultSet.TYPE_SCROLL_SENSITIVE还是看不到数据库更新后的数据的原因。
一般原因有两个:
1 第一个原因,我估计这很可能是我们非常容易忽视的一个原因,因为我们可能会先入为主,认为所有数据库的jdbc都会支持
ResultSet.TYPE_SCROLL_SENSITIVE这种类型。可能是文档看的不够详细,不过我觉得就是mysql的文档根本就没有写它这些
内容(因为我记得我在mysql-connector-java-5.1.12的pdf文档里还搜了
ResultSet.TYPE_SCROLL_SENSITIVE,就只搜到了一条不相干的内容),结果我中大奖了,我想当然的认为mysql-
connector-java-5.1.12是支持ResultSet.TYPE_SCROLL_SENSITIVE的,但结果它是不支持的。这个可以使
用以下程序来得到:
conn = DriverManager.getConnection(connStr);
DatabaseMetaData dm=conn.getMetaData();
dm.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE);
dm.supportsResultSetConcurrency(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
DatabaseMetaData是java.sql包中的一个接口,使用connection的getMetaData方法得到它的实例。调用它的那两
个方法会返回boolean值,就可以看到结果。在mysql里它返回的就是false,而在微软的jdbc里,它就会返回true。
另外要提的一点是,mysql做的程序和微软比,还是有差距,做的程序不严谨,不好用,至少在这个问题上我是这样觉得的,当使用
createStatement语句创建Statement的时候,如果传入的关于ResultSet的设置参数不被支持的话,微软的程序会直接抛错告诉
你这种类型不被支持,但是mysql明明不支持ResultSet.TYPE_SCROLL_SENSITIVE,它还是照常继续运行,一点提示都没有,
最后程序出了问题看不到正确的结果,要不是我阴差阳错的在java官网上看到一篇文章提到了DatabaseMetaData这个接口,我估计我找死,我
也找不出是这个原因。看来做事真的是不能先入为主。

确定了jdbc是不是支持ResultSet.TYPE_SCROLL_SENSITIVE后,再进行测试,但还是看不到效果。这个第二个原因是因为
resultset的FetchSize默认是0,这样就会忽略这个设置,而任由不同数据库的jdbc根据它自己的情况自行取数据。取得数据然后会被放在
缓存里,所以当resultset的游标没有超出缓存的数据范围的时候,它只会从缓存里取数据,而不会从数据库取数据,这样当然就看不到更新的数据。
ResultSet的FetchSize可以使用Statement的setFetchSize来设置。代码如下:
this.stmt.setFetchSize(1);
rs=this.stmt.executeQuery(sql);
在取resultset之前设置FetchSize表示每次只取一行,这样就能看到ResultSet.TYPE_SCROLL_SENSITIVE的效果了。
关于缓存数据这部分,可以看java官网上的文档http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame5.html

5.9
Refetching a row
Some applications may need to see up-to-the-second changes
that have been made to a row. Since a JDBC driver can do prefetching and caching
of data that is read from the underlying database (see
ResultSet.setFetchSize()), an application may not see the very latest changes
that have been made to a row, even when a sensitive result set is used and
updates are visible. The ResultSet.refreshRow() method is provided to allow an
application to request that a driver refresh a row with the latest values stored
in the database. A JDBC driver may actually refresh multiple rows at once if the
fetch size is greater than one. Applications should exercise restraint in
calling refreshRow(), since calling this method frequently will likely slow
performance.

总结一下:
ResultSet.TYPE_SCROLL_SENSITIVE失效的原因有两个:
1 jdbc本身不支持,这也告诫我们做事情不能先入为主想当然,还有就是写程序不要像mysql那样不严谨,不支持也没有任何提示,还是学微软的风格比较好。
2 因为数据被缓存了,没有从数据库读取更新后的数据。
累!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值