Java JDBC结果集的处理
结果集指针的移动
while (resultset.next()){ //...... }
指针最初指向第一条记录之前,next()是指向下一个位置,返回的是boolean值,true表示有内容(记录),false表示无内容(false)。
如果当前是指向最后一条记录,next()就指向最后一条记录之后,返回false,退出循环,遍历完成。
准确地说,应该叫做游标(cursor),学c++的时候叫指针(pointer)叫习惯了......
//下面3种都是相对当前位置的移动 resultset.next(); //指针移到下一个位置 resultset.previous(); //指针移到前一个位置 resultset.relative(2); //相对当前位置后移2步 resultset.relative(-1); //正数表示后移,负数表示前移。相对当前位置前移1步。 //下面5种都是绝对位置的移动 resultset.absolute(n); //指向结果集中的第n+1条记录,n是下标,0表示第一条记录
resultset.first(); //指针移到第一条记录上 resultset.last(); //指针移到最后一条记录上 resultset.beforefirst(); //指针移到第一条记录之前 resultset.afterlast(); //指针移到最后一条记录之后 //对应的判断方法 resultset.isfirst(); resultset.islast(); resultset.isbeforefirst(); resultset.isafterlast();
获取结果集中的记录总数
1 if(resultset.last()) { //指针移到最后一条记录上,使用if是因为结果集可能是空的,要先判断 2 system.out.println(resultset.getrow()); //getrow()是返回当前记录是结果集中的第几条记录,int型 3 }
离线查询
如果要长期使用结果集中的数据,以前有2种方式:
- 一直保持数据库连接,不关闭。如果保持的连接很多,服务器、数据库的性能都会受到影响。
- 将结果集中的记录存储到多个javabean中,数据库没影响,但要编写javabean,写代码遍历结果集,将每行记录赋给javabean,自己写代码的话很麻烦。如果记录很多,创建大量的javabean是很花时间的,jvm要管理大量的javabean对象,开销很大,程序性能降低;且操作结果集比操作javabean的集合要简单。
离线查询:在本地搞一个结果集的副本,关闭结果集、数据库连接,使用本地的这个副本。
resultset接口有子接口rowset,rowset有子接口cachedrowset。离线查询就是通过cachedrowset来实现的。
1 //从properties文件中加载数据库配置 2 properties properties = new properties(); 3 inputstream inputstream =class.forname("test.test").getresourceasstream("/mysql.properties"); 4 properties.load(inputstream); 5 6 string driver = properties.getproperty("driver"); 7 string url = properties.getproperty("url"); 8 string user = properties.getproperty("user"); 9 string pwd=properties.getproperty("password"); 10 11 class.forname(driver); 12 connection connection = drivermanager.getconnection(url, user, pwd); 13 14 string sql = "select * from student_tb"; 15 preparedstatement preparedstatement = connection.preparestatement(sql); 16 resultset resultset = preparedstatement.executequery(); 17 18 //离线查询 19 rowsetfactory rowsetfactory = rowsetprovider.newfactory(); //通过rowsetprovider的静态方法创建rowsetfactory对象 20 cachedrowset cachedrowset = rowsetfactory.createcachedrowset(); //创建cachedrowset对象 21 cachedrowset.populate(resultset); //使用结果集填充cachedrowset 22 //cachedrowset.populate(resultset,2); //可指定从结果集的第几条记录开始填充,默认为1,从第一条记录开始填充 23 24 //关闭数据库资源 25 resultset.close(); 26 preparedstatement.close(); //关闭statement|preparedstatement对象 27 connection.close(); 28 29 //cachedrowset是resultset的孙接口,继承了resultset的属性、方法,使用方式和resultset一样 30 while(cachedrowset.next()){ 31 int id = cachedrowset.getint("id"); 32 string name =cachedrowset.getstring("name"); 33 int age = cachedrowset.getint("age"); 34 float score = cachedrowset.getfloat("score"); 35 system.out.println(id+"\t"+name+"\t"+age+"\t"+score); 36 }
更新结果集
resultset默认是可滚动的、不可更新的,不能删除、修改结果集中的记录。如果要获取可更新的结果集(可增删改结果集中的记录),需要在创建statement | preparedstatement对象时传入2个额外的参数。
cachedrowset默认是可滚动的、可更新的,可删除、修改cachedrowset中的记录。
可滚动指的是可以使用next()、first()、last()、absolute()等方法移动游标,不可滚动指的是只能使用next()来移动游标。
可更新的resultset:
1 //从properties文件中加载数据库配置 2 properties properties = new properties(); 3 inputstream inputstream =class.forname("test.test").getresourceasstream("/mysql.properties"); 4 properties.load(inputstream); 5 6 string driver = properties.getproperty("driver"); 7 string url = properties.getproperty("url"); 8 string user = properties.getproperty("user"); 9 string pwd=properties.getproperty("password"); 10 11 class.forname(driver); 12 connection connection = drivermanager.getconnection(url, user, pwd); 13 14 string sql = "select * from student_tb"; 15 /* 16 获取可更新的结果集 17 18 第二个参数指定结果集是否是可滚动的: 19 resultset.type_forward_only 不可滚动,只能使用next()移动游标 20 resultset.type_scroll_sensitive 可滚动,底层数据的变化会同步到结果集(需要底层数据库驱动的支持) 21 resultset.type_scroll_insensitive 可滚动,底层数据的变化不会同步到结果集 22 23 第三个参数指定结果集是否是可更新的: 24 resultset.concur_read_only 默认值,只读 25 resultset.concur_updatable 可更新(对结果集中的记录可增删改) 26 */ 27 preparedstatement preparedstatement = connection.preparestatement(sql, resultset.type_scroll_sensitive,resultset.concur_updatable); 28 resultset resultset = preparedstatement.executequery(); 29 30 while (resultset.next()){ 31 int id = resultset.getint("id"); 32 if(id==5){ 33 //updatexxx()修改当前记录某个字段的值,第一个参数指定列(字段),可以用列名或列索引,第二个参数指定新值 34 resultset.updatestring("name","chy"); //修改当前记录的name字段的值为chy 35 resultset.updaterow(); //将对结果集中记录的改同步到数据库中,改不会自动同步到数据库中,需要手动同步 36 } 37 else if(id==10){ 38 resultset.deleterow(); //删除当前记录(删除当前行),删会自动同步到数据库中 39 } 40 } 41 42 //往结果集中插入一行 43 resultset.movetoinsertrow(); 44 /* 45 通过updatexxx()设置插入行的字段值 46 47 resultset.updateint("id",100); 48 如果不设置主键列(id)的值,默认插到结果集的末尾, 49 如果结果集中没有id=100的记录,但数据库中有,同步到数据库时会出错 50 */ 51 resultset.updatestring("name","coco"); 52 resultset.updateint("age",20); 53 resultset.updateint("score",90); 54 55 resultset.insertrow(); //将增同步到数据库 56 57 //这期间需要保持数据库连接 58 59 resultset.close(); 60 preparedstatement.close(); 61 connection.close();
update、insert需要手动同步,delete却是自动同步,why?
因为delete是一步操作,update、insert均是多步操作,update可能要updatexxx()修改多个字段,insert可能要updatexxx()插入多个字段的值,jvm不知道你的updatexxx()写完了没有,所以要写resultset.updaterow()|insertrow(); 告诉jvm:我对这条记录的改|插入字段已经弄完了,jvm才会同步到数据库中。
注意是updaterow()、insertrow()、deleterow(),是row,不是rows,同步的只是一条记录。
所以每操作完一条记录,该手动同步的就要手动同步,不能想着一次性同步所有更新。
cachedrowset的更新操作:
1 //从properties文件中加载数据库配置 2 properties properties = new properties(); 3 inputstream inputstream =class.forname("test.test").getresourceasstream("/mysql.properties"); 4 properties.load(inputstream); 5 6 string driver = properties.getproperty("driver"); 7 string url = properties.getproperty("url"); 8 string user = properties.getproperty("user"); 9 string pwd=properties.getproperty("password"); 10 11 class.forname(driver); 12 connection connection = drivermanager.getconnection(url, user, pwd); 13 14 string sql = "select * from student_tb"; 15 preparedstatement preparedstatement = connection.preparestatement(sql); //cachedrowset默认就是可更新的,不必传额外的参数 16 resultset resultset = preparedstatement.executequery(); 17 resultset.next(); 18 19 //离线结果集 20 rowsetfactory rowsetfactory = rowsetprovider.newfactory(); 21 cachedrowset cachedrowset = rowsetfactory.createcachedrowset(); 22 cachedrowset.populate(resultset); 23 resultset.close(); 24 preparedstatement.close(); 25 connection.close(); 26 27 while (cachedrowset.next()){ 28 int id = cachedrowset.getint("id"); 29 if (id==15){ 30 //删除记录 31 cachedrowset.deleterow(); 32 } 33 else if(id==20){ 34 //修改记录 35 cachedrowset.updatestring("name","chy1"); 36 cachedrowset.updaterow(); 37 } 38 } 39 40 connection connection1 = drivermanager.getconnection(url, user, pwd); //连接已关闭了,需要重新获取一个连接 41 connection1.setautocommit(false); //将新连接的自动提交设置为false 42 cachedrowset.acceptchanges(connection1); //同步到数据库。
对cachedrowset使用deleterow()、updaterow()不会立刻同步到数据库中,因为连接已经关闭了,同步不了。
这2个方法相当于把这些同步操作放在一个队列中,当 cachedrowset.acceptchanges(connection1); 传入一个连接时,就依次执行队列中的同步操作。
cachedrowset不能插入一条记录,因为cachedrowset是批量同步的(队列),插入操作可能会受到队列中其他记录的影响。
cachedrowset是默认可更新的,不是默认自动提交的。
更新操作要求结果集满足2个条件:
- 结果集只能是单表查询的结果集
- 结果集中必须含有主键列
分页
分页有3种实现方式。
1、使用sql语句来限定结果集范围
1 string sql = "select * from student_tb where id>? and id<?"; 2 preparedstatement preparedstatement = connection.preparestatement(sql); 3 preparedstatement.setint(1,0); 4 preparedstatement.setint(2,11); 5 resultset resultset = preparedstatement.executequery();
这种方式不好,因为区间上的某些记录可能被删除了,id没了。比如1页10条记录,[1,10],但id=5,id=8的记录被删除了(比如注销账户),实际取到的就比10条少。
1 string sql = "select * from student_tb limit ?,?"; 2 preparedstatement preparedstatement = connection.preparestatement(sql); 3 preparedstatement.setint(1,0); //从满足条件的记录中的第1条记录起(参数是下标) 4 preparedstatement.setint(2,10); //只取10条记录 5 /* 6 第一个参数可缺省,默认为0,从第一条记录起。 7 string sql = "select * from student_tb limit 100"; 只取100条记录 8 */
能取足10条,但limit是mysql的特性,对应的sql server特性是top、oracle特性是rownum,不跨数据库。
2、使用游标来实现
1 string sql = "select * from student_tb"; 2 preparedstatement preparedstatement = connection.preparestatement(sql); 3 resultset resultset = preparedstatement.executequery(); 4 5 int start=0; //开始位置 6 int pagesize=10; //每页显示多少条记录 7 resultset.absolute(start); //先转到起始处。参数是下标,0表示第一条记录 8 while (resultset.next()){ 9 //...... 10 system.out.println(resultset.getrow()); 11 if(resultset.getrow()==start+pagesize) //getrow()是获取当前记录是结果集中的第几条记录,第一条就是1。也可以设置计数器来判断 12 break; 13 }
3、使用离线查询来实现
1 //离线结果集 2 rowsetfactory rowsetfactory = rowsetprovider.newfactory(); 3 cachedrowset cachedrowset = rowsetfactory.createcachedrowset(); 4 cachedrowset.setpagesize(10); //设置每页显示多少条记录,其实设置的是cachedrowset的容量 5 cachedrowset.populate(resultset,1); //设置从结果集中的第几条记录开始填充。cachedrowset中的记录是结果集中的[1,10]条 6 resultset.close(); 7 preparedstatement.close(); 8 connection.close(); 9 10 while (cachedrowset.next()){ 11 //...... 12 }