昨天一下午,我的一个同事都在为一个让他费解的问题而感到烦恼。
他从数据库中获得一个数据的结果集,而后去遍历这个结果集,在这个过程当中,每条数据都有一个ID,这些ID有重复的,我们假设有五条数据,ID分别为1,2,3,3,4,然后通过ID到另一个表中查询数据,一个ID可以查到唯一的一条数据,检查这个查到的数据的名为Link的字段,如果为空,则给其赋值,使之不为空。
他认为第一次通过3查到的数据的link字段在被修改后,那么第二次通过3查到的数据,其link字段就不应当为空,在这里他通过判断Link字段不为空则打印一条语句。但是这条本应打印的语句没有被执行。
今天早上帮着他查找产生这个怪异问题的原因,发现了两个特殊的地方。
一是他最开始获得的结果集的内容依赖于他之后所查表的数据的link的字段是否为空,简单的讲,程序跑完后,他再次获得结果集时,结果集已经为空,因为所有的第二张表里的数据link字段已经不为空。
我们最开始也清楚这个问题,但是我们认为结果集被查询出来以后,已经和数据库分离,我们认为这个结果集里始终有五条数据,但实际情况是第二个ID为3的数据在这个过程中消失了,我们在修改link字段前打印结果集里的内容,发现只打印了一个ID为3的内容,如果把修改link字段的代码注释掉,则可以打印完成的5条数据。
通过这一步,我们断定修改link导致了最开始获得的结果集发生了变化,因为结果集里的内容依赖于这个被修改的字段,进一步我们可以推理出通过ResultSet方法获得结果集和数据库还存在着关系,使得另一张表里的link字段在改变后,结果集也随之改变。
在网上搜索了一些资料终于明白,ResultSet需要设置一些属性,才能保证获得结果集是只读的,不再受数据库更新的影响。
conn.createStatement(Result.TYPE_SCROLL_INSENITIVE, ResultSet.CONCUR_READ_ONLY);
经过这样的设置后,就可以保证我们获得结果集不再受数据库中数据更新的影响。
createStatement返回的Statement对象是执行sql语句用的
第一个参数可以取值为
ResultSet.RTYPE_FORWORD_ONLY,只可向前滚动;
ResultSet.TYPE_SCROLL_INSENSITIVE,双向滚动,但不及时更新,就是如果数据库里的数据修改过,并不在ResultSet中反应出来。
ResultSet.TYPE_SCROLL_SENSITIVE,双向滚动,并及时跟踪数据库的更新,以便更改ResultSet中的数据。
第二个参数可以取值为
ResultSet.CONCUR_READ_ONLY:这是缺省值,指定不可以更新 ResultSet
ResultSet.CONCUR_UPDATABLE:指定可以更新 ResultSet
第一个参数可以取值为
ResultSet.RTYPE_FORWORD_ONLY,只可向前滚动;
ResultSet.TYPE_SCROLL_INSENSITIVE,双向滚动,但不及时更新,就是如果数据库里的数据修改过,并不在ResultSet中反应出来。
ResultSet.TYPE_SCROLL_SENSITIVE,双向滚动,并及时跟踪数据库的更新,以便更改ResultSet中的数据。
第二个参数可以取值为
ResultSet.CONCUR_READ_ONLY:这是缺省值,指定不可以更新 ResultSet
ResultSet.CONCUR_UPDATABLE:指定可以更新 ResultSet
我们两个人都还没有毕业,目前在公司里实习,他做开发,我做测试,偶尔会就一些问题探讨一下。经过近两个小时的排查,总算是把问题解决了。
其实这算不上是什么BUG,只能说我们的知识面还不够广,要像做好测试,看来需要广泛的学习各种与软件有关的知识,另一方面,发现问题时一定要敢于大胆的去假设,此前,我们一直认为ResultSet返回的结果集是不变的,认为它是查询出来的,已经存在于某个与数据库无关的地方,直到我大胆的提出结果集与数据库仍然存在联系,我们才开始怀疑这个问题。
当我提出时,他更倾向于原来的想法,即这些结果是不变的,仍在受以往经验的影响,他更相信程序,而不相信自己通过调试时发现的问题而逐步推导出的结论,以至于他昨天整整一下午加一个晚上都在从程序的控制流程上找原因。