结果集指针的移动
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 }