Java JDBC结果集的处理

Posted chy18883701161

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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         

 

以上是关于Java JDBC结果集的处理的主要内容,如果未能解决你的问题,请参考以下文章

jdbc利用java反射实现结果集得到处理:bug:argument type mismatch

JDBC基础--[JDBC概述,JDBC的搭建,PreparedStatement和Statement执行SQL语句,结果集处理]

Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法

JDBC处理大结果集

使用 JDBC 可调用 stmt 存储 proc 时返回的结果集的顺序是啥?

JDBC中结果集的“Kludge”[重复]