13.6.2 离线RowSet
在使用ResultSet的时代,程序查询得到ResultSet之后必须立即读取或处理它对应的记录,否则旦Connection关闭,再去通过ResultSet读取记录就会引发异常。在这种模式下,JDBC编程十分痛苦.
假设应用程序架构被分为两层:数据访问层和视图显示层,当应用程序在数据访问层查询得到ResultSet之后,对ResultSet的处理有如下两种常见方式。
1.使用迭代访问ResultSet里的记录,并将这些记录转换成Java Bean,再将多个Java Bean封装成个List集合,也就是完成”ResultSet到Java 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 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 { Class.forName(driver); Connection conn = DriverManager.getConnection(url, user, pass); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); RowSetFactory factory = RowSetProvider.newFactory(); CachedRowSet cachedRs = factory.createCachedRowSet(); 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); rs.acceptChanges(conn); } }
|
上面程序中的①号代码调用了RowSet的populate(ResultSet rs)方法来包装给定的ResultSet,接下来的粗体字代码关闭了ResultSet、 Statement、 Connection等数据库资源。如果程序直接返回ResultSet,那么这个ResultSet无法使用:这是因为底层的Connection已经关闭了;但程序返回的是CachedRowSet,它是一个离线RowSet,因此程序依然可以读取、修改RowSet中的记录。
运行该程序,可以看到在Connection关闭的情况下,程序依然可以读取、修改RowSet里的记录.
为了将程序对离线RowSet所做的修改同步到底层数据库,程序在调用RowSet的acceptChanges()方法时必须传入Connection.