13.6.2 离线RowSet

13.6.2 离线RowSet

在使用ResultSet的时代,程序查询得到ResultSet之后必须立即读取或处理它对应的记录,否则旦Connection关闭,再去通过ResultSet读取记录就会引发异常。在这种模式下,JDBC编程十分痛苦.
假设应用程序架构被分为两层:数据访问层和视图显示层,当应用程序在数据访问层查询得到ResultSet之后,对ResultSet的处理有如下两种常见方式。

  • 1.使用迭代访问ResultSet里的记录,并将这些记录转换成Java Bean,再将多个Java Bean封装成个List集合,也就是完成”ResultSetJava Bean集合”的转换。转换完成后可以关闭Connection等资源,然后将Java bean集合传到视图显示层,视图显示层可以显示查询得到的数据。

  • 2.直接将ResultSet传到视图显示层——这要求当视图显示层显示数据时,底层Connection必须直处于打开状态,否则ResultSet无法读取记录。

  • 第一种方式比较安全,但编程十分烦琐;

  • 第二种方式则需要Connection一直处于打开状态,这不仅不安全,而且对程序性能也有较大的影响。

通过使用离线RowSet可以十分”优雅”地处理上面的问题,离线RowSet会直接将底层数据读入内存中,封装成RowSet对象,而RowSet对象则完全可以当成Java Bean来使用。因此不仅安全,而且编程十分简单。CachedRowSet是所有离线RowSet的父接口,因此下面以CachedRowSet为例进行介绍。看下面程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.util.*;
import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.sql.rowset.*;

public class CachedRowSetTest
{
private static String driver;
private static String url;
private static String user;
private static String pass;
public void initParam(String paramFile) throws Exception
{
// 使用Properties类来加载属性文件
Properties props = new Properties();
props.load(new FileInputStream(paramFile));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}

public CachedRowSet query(String sql) throws Exception
{
// 1.加载驱动
Class.forName(driver);
// 2.获取数据库连接
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 3.使用RowSetProvider创建RowSetFactory
RowSetFactory factory = RowSetProvider.newFactory();
// 4.创建默认的CachedRowSet实例
CachedRowSet cachedRs = factory.createCachedRowSet();
// 5.使用ResultSet装填RowSet
cachedRs.populate(rs); // ①
// 关闭资源
rs.close();
stmt.close();
conn.close();
return cachedRs;
}
public static void main(String[] args) throws Exception
{
CachedRowSetTest ct = new CachedRowSetTest();
ct.initParam("mysql.ini");
CachedRowSet rs = ct.query("select * from student_table");
rs.afterLast();
// 向前滚动结果集
while (rs.previous())
{
System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t"
+ rs.getString(3));
if (rs.getInt("student_id") == 3)
{
// 修改指定记录行
rs.updateString("student_name", "孙悟空");
rs.updateRow();
}
}
// 重新获取数据库连接
Connection conn = DriverManager.getConnection(url, user, pass);
conn.setAutoCommit(false);
// 把对RowSet所做的修改同步到底层数据库
rs.acceptChanges(conn);
}
}

上面程序中的①号代码调用了RowSetpopulate(ResultSet rs)方法来包装给定的ResultSet,接下来的粗体字代码关闭了ResultSetStatementConnection等数据库资源。如果程序直接返回ResultSet,那么这个ResultSet无法使用:这是因为底层的Connection已经关闭了;但程序返回的是CachedRowSet,它是一个离线RowSet,因此程序依然可以读取、修改RowSet中的记录。

运行该程序,可以看到在Connection关闭的情况下,程序依然可以读取、修改RowSet里的记录.
为了将程序对离线RowSet所做的修改同步到底层数据库,程序在调用RowSetacceptChanges()方法时必须传入Connection.