Java 6 RowSet 使用完全剖析
2010-01-25 23:25
489 查看
Java 6 RowSet 使用完全剖析
C# 提供了 DataSet,可以将数据源中的数据读取到内存中,进行离线操作,然后再同步到数据源。同样,在 Java 中也提供了类似的实现,即 RowSet。javax.sql.rowset
包下,定义了五个不同的 RowSet 接口,供不同的场合使用。本文将分别对这五个 RowSet 的使用场合以及详尽用法进行介绍,并且描述使用中可能出现的问题,以提醒读者在实际使用时绕开这些问题。
RowSet 简介javax.sql.rowset自 JDK 1.4 引入,从 JDK 5.0 开始提供了参考实现。它主要包括 CachedRowSet,WebRowSet,FilteredRowSet,JoinRowSet 和 JdbcRowSet。 除了 JdbcRowSet 依然保持着与数据源的连接之外,其余四个都是 Disconnected RowSet。
相比较
java.sql.ResultSet而言,RowSet 的离线操作能够有效的利用计算机越来越充足的内存,减轻数据库服务器的负担,由于数据操作都是在内存中进行然后批量提交到数据源,灵活性和性能都有了很大的提高。RowSet 默认是一个可滚动,可更新,可序列化的结果集,而且它作为 JavaBeans,可以方便地在网络间传输,用于两端的数据同步。
|
CachedRowSet | 最常用的一种 RowSet。其他三种 RowSet(WebRowSet,FilteredRowSet,JoinRowSet)都是直接或间接继承于它并进行了扩展。它提供了对数据库的离线操作,可以将数据读取到内存中进行增删改查,再同步到数据源。可串行化,可作为 JavaBeans 在网络间传输。支持事件监听,分页等特性。 |
WebRowSet | 继承自 CachedRowSet,并可以将 WebRowSet 写到 XML 文件中,也可以用符合规范的 XML 文件来填充 WebRowSet。 |
FilteredRowSet | 通过设置 Predicate(在 javax.sql.rowset包中),提供数据过滤的功能。可以根据不同的条件对 RowSet 中的数据进行筛选和过滤。 |
JoinRowSet | 提供类似 SQL JOIN 的功能,将不同的 RowSet 中的数据组合起来。目前在 Java 6 中只支持内联(Inner Join)。 |
JdbcRowSet | 对 ResultSet 的一个封装,使其能够作为 JavaBeans 被使用,是唯一一个保持数据库连接的 RowSet。 |
填充 CachedRowSet 的两种方式
CachedRowSet 提供了两个用来获取数据的方法,一个是
execute(),另一个是
populate(ResultSet)。
使用
execute()填充 CachedRowSet 时,需要设置数据库连接参数和查询命令
command,如下示例代码:
清单 1. 使用 execute()
cachedRS.setUrl(DBCreator.DERBY_URL); cachedRS.setCommand(DBCreator.SQL_SELECT_CUSTOMERS); // derby 默认用户名和密码都是 "APP",也可以不设置。 cachedRS.setUsername("APP"); //$NON-NLS-1$ cachedRS.setPassword("APP"); //$NON-NLS-1$ cachedRS.execute(); |
command,用结果集填充 cachedRS,最后关闭数据库连接。
execute()还可以直接接受一个已经打开的数据库连接,假设 conn 为一个已经打开的数据库连接,下段示例代码与上段代码结果一致:
清单 2. 使用 execute(Connection)
cachedRS.execute(conn); cachedRS.setCommand(DBCreator.SQL_SELECT_CUSTOMERS); cachedRS.execute(); |
populate(ResultSet)。
清单 3. 使用 populate(ResultSet)
ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_CUSTOMERS); cachedRS.populate(rs); rs.close(); |
更新、删除、插入数据
更新数据。先把游标(cursor)移到要更新的行,根据每列的类型调用对应的
updateXXX(index, updateValue),再调用
updateRow()方法。此时,只是在内存中更新了该行,同步到数据库需要调用方法
acceptChanges()或
acceptChanges(Connection)。如果 CachedRowSet 中保存着原数据库连接信息,则可以调用
acceptChanges();否则,则应该传入可用的数据库连接或重新设置数据库连接参数。下段示例代码更新第一行的第二列。
清单 4. 更新
cachedRS.first(); cachedRS.updateString(2, "Hello"); //$NON-NLS-1$ cachedRS.updateRow(); cachedRS.acceptChanges(); |
deleteRow(),再同步回数据库即可。
清单 5. 删除
cachedRS.last(); cachedRS.deleteRow(); cachedRS.acceptChanges(); |
getShowDeleted()和
setShowDeleted(boolean value)两个方法来读取和设置这个属性。showDeleted 是用来判断被标记为删除且尚未同步到数据库的行在 CachedRowSet 中是否可见。true 为可见,false 为不可见。默认值为 false。
插入数据。插入操作稍微比更新和删除复杂。先看下段示例代码。
清单 6. 新增
cachedRS.last(); cachedRS.moveToInsertRow(); cachedRS.updateInt(1, 3); cachedRS.updateString(2, "Bob"); //$NON-NLS-1$ cachedRS.updateString(3, "A new user"); //$NON-NLS-1$ cachedRS.insertRow(); cachedRS.moveToCurrentRow(); cachedRS.acceptChanges(); |
moveToInsertRow(),然后调用
updateXXX()设置各列值,再调用
insertRow(),最后再把游标移到当前行。注意一定要遵循这个步骤,否则将抛出异常。
冲突处理
当我们使用 CachedRowSet 更新数据库时,有可能因为内存中的数据过期而产生冲突。此时更新数据库的方法
acceptChanges()会抛出
SyncProviderException,由此我们可以捕获产生冲突的原因并手动进行解决。
清单 7. 冲突
ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_CUSTOMERS); CachedRowSet cachedRS = new CachedRowSetImpl(); cachedRS.populate(rs); cachedRS.setUrl(DBCreator.DERBY_URL); // 修改数据库中的数据 stmt.executeUpdate("UPDATE CUSTOMERS SET NAME = 'Terry' WHERE ID = 1"); // 在 CachedRowSet 中更新同一行 cachedRS.absolute(1); cachedRS.updateString(3, "Tom is not VIP"); cachedRS.updateRow(); SyncResolver resolver = null; try { cachedRS.acceptChanges(); } catch (SyncProviderException e) { resolver = e.getSyncResolver(); } while (resolver.nextConflict()) { System.out.println(resolver.getStatus()); } |
SyncResolver 继承了 RowSet 接口,我们可以像使用一般 RowSet 一样操作它。SyncResolver 的实例拥有与正在同步的 RowSet 相同的行数和列数。使用
nextConflict()和
previousConflict()可以遍历所有产生的冲突,
getStatus()可以获得冲突的类型。在 SyncResolve 中定义了四种类型,分别是:DELETE_ROW_CONFLICT,INSERT_ROW_CONFLICT,NO_ROW_CONFLICT,UPDATE_ROW_CONFLICT。上例中产生的是 UPDATE_ROW_CONFLICT。
注:目前 Sun JDK 对 SyncResolver 的支持非常有限,只实现了 SyncResolver 接口中定义的方法,调用从 RowSet 接口继承的方法都会抛出
UnsupportedOperationException异常;
getConflictValue()返回都是 null。
事件监听
一个监听器需要实现 RowSetListener 接口。RowSetListener 支持三种事件监听:cursor moved、row changed 和 rowSet changed。假定 Listener 实现了 RowSetListener 接口,看示例代码。
清单 8. 注册事件监听器
Listener listener = new Listener(); cachedRS.addRowSetListener(listener); updateOnRowSet(cachedRS); cachedRS.removeRowSetListener(listener); |
updateOnRowSet()所做的操作就是将游标移到第一行,更新,再同步回数据库。在这个方法中,依次触发了 Listener 的三个事件。下表列出了 CachedRowSet 中会触发监听器的所有方法。
表 4. CachedRowSet 中会触发监听器的方法
cursor moved | row changed | rowSet changed | |
---|---|---|---|
absolute() | √ | ||
relative() | √ | ||
next() | √ | ||
previous() | √ | ||
first() | √ | ||
last() | √ | ||
beforeFirst() | √ | ||
afterLast() | √ | ||
updateRow() | √ | ||
deleteRow() | √ | ||
insertRow() | √ | ||
undoDelete() | √ | ||
undoUpdate() | √ | ||
undoInsert() | √ | ||
populate() | √ | ||
acceptChanges() | √ | ||
acceptChanges(Connection) | √ | ||
execute() | √ | ||
execute(Connection) | √ | ||
nextPage() | √ | ||
previousPage() | √ | ||
restoreOriginal() | √ | ||
release() | √ |
事务对于保证数据的一致性是非常重要的。CachedRowSet 专门提供了处理事务的接口,从而可以保证同步数据的原子性和一致性。CachedRowSet 默认是不使用事务的。
清单 9. 事务代码一
cachedRS.absolute(1); // 第一列不能为 null,更新时将产生冲突 cachedRS.updateNull(1); cachedRS.updateRow(); cachedRS.next(); cachedRS.updateString(2, "Terry"); cachedRS.updateRow(); try { cachedRS.acceptChanges(conn); } catch (SyncProviderException e) { // expected } rs = stmt.executeQuery(DBCreator.SQL_SELECT_CUSTOMERS); cachedRS = new CachedRowSetImpl(); cachedRS.populate(rs); printRowSet(cachedRS); |
清单 10. 事务代码二
cachedRS.absolute(1); cachedRS.updateNull(1); cachedRS.updateRow(); cachedRS.next(); cachedRS.updateString(2, "Terry"); cachedRS.updateRow(); conn.setAutoCommit(false); try { cachedRS.acceptChanges(conn); cachedRS.commit(); } catch (SyncProviderException e) { // expected cachedRS.rollback(); } conn.setAutoCommit(true); rs = stmt.executeQuery(DBCreator.SQL_SELECT_CUSTOMERS); cachedRS = new CachedRowSetImpl(); cachedRS.populate(rs); printCachedRowSet(cachedRS); |
需要注意的是,如果需要手动控制事务的范围,在调用 execute 或 acceptChanges 时必须传入 Connection,否则再调用 CachedRowSet 的
commit()或
rollback()会抛
NullPointerException。实际上 CachedRowSet 依然是通过在内部保存 Connection 的引用来实现事务操作的。
分页
由于 CachedRowSet 是将数据临时存储在内存中,因此对于许多 SQL 查询,会返回大量的数据。如果将整个结果集全部存储在内存中会占用大量的内存,有时甚至是不可行的。对此 CachedRowSet 提供了分批从 ResultSet 中获取数据的方式,这就是分页。应用程序可以简单的通过 setPageSize 设置一页中数据的最大行数。也就是说,如果页大小设置为 5,一次只会从数据源获取 5 条数据。下面的代码示范了如何进行简单分页操作。(分页部分代码默认 ORDERS 表中有 10 条数据)
清单 11. 分页代码一
ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS); CachedRowSet cachedRS = new CachedRowSetImpl(); // 设置页大小 cachedRS.setPageSize(4); cachedRS.populate(rs, 1); while (cachedRS.nextPage()) { printRowSet(cachedRS); } while (cachedRS.previousPage()) { printRowSet(cachedRS); } |
用来填充 CachedRowSet 的 ResultSet 必须是可滚动的(Scrollable)。
populate 必须使用有两个参数的版本,否则无法进行分页。读者可以将
cachedRS.populate(rs, 1);换成
cachedRS.populate(rs);看看会有怎样的情况发生。
ResultSet rs必须在遍历完毕后才能关闭,否则翻页遍历时会抛
SQLException。
我们注意到在使用分页遍历数据集时,
nextPage()是最先被调用的,也就是说页与行相似,最开始的页游标是指向第 0 页的,通过
nextPage()方法移到第一页,这样就可以用非常简洁的代码遍历数据集。那么如果在第 0 页时(第一次调用 nextPage)之前使用 next 遍历当前页会是怎样的结果呢?
清单 12. 分页代码二
ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS); CachedRowSet cachedRS = new CachedRowSetImpl(); // 设置页大小 cachedRS.setPageSize(4); cachedRS.populate(rs, 1); printRowSet(cachedRS); while (cachedRS.nextPage()) { printRowSet(cachedRS); } |
setMaxRows 可以设置 CachedRowSet 可遍历的最大行数。下面是 setMaxRows 和 setPageSize 同时使用的例子。
清单 13. 分页代码三
ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS); CachedRowSet cachedRS = new CachedRowSetImpl(); cachedRS.setPageSize(4); cachedRS.setMaxRows(7); cachedRS.populate(rs, 1); while (cachedRS.nextPage()) { printRowSet(cachedRS); } rs.close(); |
清单 14. 清单 13 中的代码执行结果
The data in RowSet: 1 1 Book 2 1 Compute 3 2 Phone 4 2 Java The data in RowSet: 5 2 Test 6 1 C++ 7 2 Perl |
清单 15. 分页代码四
ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS); CachedRowSet cachedRS = new CachedRowSetImpl(); cachedRS.setPageSize(4); cachedRS.populate(rs, 1); cachedRS.setPageSize(3); while (cachedRS.nextPage()) { printRowSet(cachedRS); } rs.close(); |
清单 16. 清单 15 中的代码执行结果
The data in RowSet: 1 1 Book 2 1 Compute 3 2 Phone 4 2 Java The data in RowSet: 5 2 Test 6 1 C++ 7 2 PerlThe data in RowSet: |
应注意的问题
在 JDK 5.0 中,当删除一行中某列值为 null 时,会抛出
NullPointerException。例如,表
CUSTOMERS中第二行第三列的值为 null。假设 cachedRS 里面填充着表
CUSTOMERS的数据,那么下段代码在 JDK 5.0 下运行时会抛出
NullPointerException。在 JDK 6.0 中,此问题已得到修正。
清单 17.
cachedRS.absolute(2); cachedRS.deleteRow(); cachedRS.acceptChanges(); |
|
WebRowSet 继承于 CachedRowSet,因此用来填充 CachedRowSet 的方式同样适用于 WebRowSet。WebRowSet 也可以读取一个符合规范的 XML 文件,填充自己。假定 CUSTOMERS.xml 是一个符合规范的 XML 文件,里面存放的是表 CUSTOMERS 的数据。
清单 18. 读取 XML 文件
WebRowSet newWebRS = new WebRowSetImpl(); newWebRS.readXml(new FileReader("CUSTOMERS.xml")); |
清单 19. 生成 XML 文件
WebRowSet webRS = new WebRowSetImpl(); CachedRowSetDemo.fillRowSetWithExecute(webRS); // 输出到XML文件 FileWriter fileWriter = new FileWriter("CUSTOMERS.xml"); webRS.writeXml(fileWriter); |
fillRowSetWithExecute(CachedRowSet)是一个静态方法,它的功能是用表
CUSTOMERS来填充传入的 CachedRowSet。
应注意的问题
按照规范,当一行被标记为更新时,在输出到 XML 文件时,应使用标签
<modifyRow>,但实际输出为
<currentRow>。
按照规范,当一行中某列被更新时,在输出到 XML 文件时,应使用标签
<updateValue>,但实际输出为
<updateRow>。
按照规范,读取 XML 文件时,如果某行标签为
<deleteRow>,在读到 WebRowSet 中时,该行应被标记为删除,实际读取后,状态丢失。
|
FilteredRowSet 继承自 WebRowSet。正如它的名字所示,FilteredRowSet 是一个带过滤功能的 RowSet。它的过滤规则在 Predicate 中定义。Predicate 也是
javax.sql.rowset包下的接口,它定义了三个方法:
boolean evaluate(Object value, int column),
boolean evaluate(Object value, String columnName),
boolean evaluate(RowSet rs)。 前两个方法主要是用来检查新插入行的值是否符合过滤规则,符合,返回 true;否则,返回 false。FilteredRowSet 在新插入行时,会用这个方法来检测。如果不符合,会抛出
SQLException。
boolean evaluate(RowSet rs)这个方法则是用来判断当前 RowSet 里面的所有数据,是否符合过滤规则。FilteredRowSet 在调用有关移动游标的方法时,会使用这个方法进行检测。只要符合过滤规则的数据才会显示出来。下面我们给出了一个非常简单的 Predicate 实现,它的过滤规则是每行第一列的值只有大于 1 的才是有效行。
清单 20. Rang.java
class Range implements Predicate { @Override public boolean evaluate(RowSet rs) { try { if (rs.getInt(1) > 1) { return true; } } catch (SQLException e) { // do nothing } return false; } @Override public boolean evaluate(Object value, int column) throws SQLException { return false; } @Override public boolean evaluate(Object value, String columnName) throws SQLException { return false; } } |
清单 21. 设置过滤器
FilteredRowSet filterRS = new FilteredRowSetImpl(); CachedRowSetDemo.fillRowSetWithExecute(filterRS); System.out.println("/*******Before set filter***********/"); CachedRowSetDemo.printRowSet(filterRS); System.out.println("/n/*******After set filter***********/"); Range range = new Range(); filterRS.setFilter(range); CachedRowSetDemo.printRowSet(filterRS); |
/*******Before set filter***********/ The data in RowSet: 1 Tom Tom is VIP. 2 Jim null /*******After set filter***********/ The data in RowSet: 2 Jim null |
SQLException。
应注意的问题
当设置了过滤器后,
FilteredRowSet.absolute(1)无论何时都返回 false。按照规范,如果第一行值不符合过滤规则,则移到第二行,依次类推,直到找到第一条符合过滤规则的结果行并返回 true,否则,返回 false。
在 JDK 5.0 下,如果 FilteredRowSet 没有设置过滤器,那么在新插入一行时会抛出
NullPointerException。在 JDK 6.0 中,已解决该问题。
|
JdbcRowSet 是对 ResultSet 的一个简单的封装,让它可以作为一个 JavaBeans 组件来使用。填充 JdbcRowSet 只能通过一种方式,即
execute()方法。之后可以通过类似于操作 ResultSet 的方法来操作 JdbcRowSet。
清单 23. JdbcRowSet
JdbcRowSet jrs = new JdbcRowSetImpl(); jrs.setCommand(DBCreator.SQL_SELECT_CUSTOMERS); jrs.setUrl(DBCreator.DERBY_URL); jrs.execute(); while (jrs.next()) { for(int i = 1; i <= jrs.getMetaData().getColumnCount(); i++) { System.out.print(jrs.getObject(i) + " "); //$NON-NLS-1$ } System.out.println(); } |
1 Tom Tom is VIP. 2 Jim null |
|
支持的联合方式
JoinRowSet 接口中对五种不同的联合方式都定义了对应的常数和判断该实现是否支持的方法,如下表所示。
表 5. 五种联合方式
联合方式 | 对应的常数 | 判断是否支持的方法(返回布尔值) |
---|---|---|
内连接(INNER JOIN) | JoinRowSet.INNER_JOIN | supportsInnerJoin() |
左外连接(LEFT OUTER JOIN) | JoinRowSet.LEFT_OUTER_JOIN | supportsLeftOuterJoin() |
右外连接(RIGHT OUTER JOIN) | JoinRowSet.RIGHT_OUTER_JOIN | supportsRightOuterJoin() |
全外连接(FULL OUTER JOIN) | JoinRowSet.FULL_JOIN | supportsFullJoin() |
交叉连接(CROSS JOIN) | JoinRowSet.CROSS_JOIN | supportsCrossJoin() |
getJoinType()返回当前的联合方式,
setJoinType(int)设置联合方式。值得注意的是,Java 5 和 Java 6 中都支持内连接 (INNER JOIN) 这一种联合方式。在 setJoinType 方法中传入除 Inner_Join 以外的任何一种联合方式都会抛出
UnsupportedOperationException的异常。
另外一点需要注意的,虽然默认的联合方式就是内连接,但是在没有显示的调用
setJoinType()之前调用
getJoinType()会抛出
ArrayIndexOutOfBoundsException的异常。 所以一般来讲,我们可以不需要调用这几个方法而直接认为 JoinRowSet 默认并且只允许的联合方式就是内连接(INNER JOIN)。
如何联合各种 RowSet
联合多个 RowSet 的方法其实就是往一个 JoinRowSet 里调用 add 方法添加其他 RowSet(这个 RowSet 可以是上面提到的五种 RowSet 中的任意一种,包括离线操作的 JdbcRowSet 和 JoinRowSet 本身)的过程。添加一个 RowSet 的同时也必须制定联合时匹配的列。JoinRowSet 接口中提供了以下几个 add 方法:
addRowSet(Joinable rowset)
addRowSet(RowSet[] rowset, int[] columnIdx)
addRowSet(RowSet[] rowset, String[] columnName)
addRowSet(RowSet rowset, int columnIdx)
addRowSet(RowSet rowset, String columnName)
下面是一个联合一个 JdbcRowSet 和一个 CachedRowSet 的简单例子。
清单 25. 使用 JoinRowSet
// 构造一个CachedRowSet并且填充CUSTOMERS表中的数据。 CachedRowSet cachedRS = new CachedRowSetImpl(); cachedRS.setUrl(DBCreator.DERBY_URL); cachedRS.setCommand(DBCreator.SQL_SELECT_CUSTOMERS); cachedRS.execute(); // 构造一个JdbcRowSet并且填充ORDERS表中的数据。 JdbcRowSet jdbcRS = new JdbcRowSetImpl(); jdbcRS.setUrl(DBCreator.DERBY_URL); jdbcRS.setCommand(DBCreator.SQL_SELECT_ORDERS); jdbcRS.execute(); // 把cachedRS添加进新构造的JoinRowSet中。 JoinRowSet joinRS = new JoinRowSetImpl(); joinRS.addRowSet(cachedRS, "ID"); //$NON-NLS-1$ // 下面这条被注释的语句会抛出ClassCastExcepion。 // joinRS.addRowSet(jdbcRS, "ID"); // 把jdbcRS添加进这个JoinRowSet中。 jdbcRS.setMatchColumn("ID"); //$NON-NLS-1$ joinRS.addRowSet(jdbcRS); // 观察结果 printRowSet(joinRS); |
The data in CachedRowSet: 2 Jim null 1 Compute 1 Tom Tom is VIP. 1 Book |
addRowSet(Joinable)这个方法(JdbcRowSet 实现了 Joinable 接口),并且在调用这个方法之前一定要先设置配对的列名或者以 1 为基数的配对列的位置,如下所示。
清单 27. 添加 JdbcRowSet
// 正确的添加一个JdbcRowSet的方法。 jdbcRS.setMatchColumn("ID"); //$NON-NLS-1$ joinRS.addRowSet(jdbcRS); |
清单 28. 查看 JoinRowSet 中列名
for (int i = 1; i <= joinRS.getMetaData().getColumnCount(); i++) { System.out.println(joinRS.getMetaData().getColumnName(i)); } |
MergedCol NAME REMARK USER_ID PRODUCT |
从前面的继承结构图可以看到,JoinRowSet 继续于 CachedRowSet。因此从理论上来看,JoinRowSet 也可以作为 CachedRowSet 来使用。但是其实 JoinRowSet 很多方法并不是简单的直接继承 CachedRowSet 中的方法,而是重写(Override)了这些方法。最典型的两个方法是 execute 和 populate。 前面已经提到过,这是两种填充一个 CachedRowSet 的方法。但是对于 JoinRowSet,这两个方法却不起作用,不应该被使用。
另外一个比较特殊的方法是 acceptChanges。当 JoinRowSet 中包含一个 CachedRowSet 的时候,这个方法可以使用,并且效果就相当于在里面的这个 CachedRowSet 里面调用。但是当 JoinRowSet 包含两个或多个 RowSet 的时候,这个方法就不起作用了。这就类似于数据库多表联结后形成的视图(View),一般是不能够进行更新操作的。
应注意的问题
虽然 JoinRowSet 提供了五个不同的 addRowSet 方法,但是并不是对于每个 RowSet 这五个方法都是可行的。这点前面已经提到过。
程序不能依赖 JoinRowSet 中的数据的顺序。JoinRowSet 接口中并没有能够控制连接结果排序的方法。它只能保证最后连接结果的正确性,但不能保证顺序。
当把一个 RowSet 加入到 JoinRowSet 中,这个作为 addRowSet 方法的参数的 RowSet 的指针位置可能发生变化。另外当这个 RowSet 是 JdbcRowSet 的时候,在通过 addRowSet 方法加入之前,如果该 JdbcRowSet 的指针位置发生变化的时候,也会影响联合的结果。请看下面这个例子(假设我们已经像前面一样构造好了一个 CachedRowSet 和一个 JdbcRowSet):
清单 30
// 添加cachedRS. JoinRowSet joinRS = new JoinRowSetImpl(); joinRS.addRowSet(cachedRS, "ID"); //$NON-NLS-1$ // 在添加jdbcRS之前,改变指针的位置。 jdbcRS.next(); jdbcRS.setMatchColumn("ID"); //$NON-NLS-1$ joinRS.addRowSet(jdbcRS); // 观察结果 printRowSet(joinRS); |
The data in CachedRowSet: 2 Jim null 1 Compute |
那么,对于 CachedRowSet,情况又是怎么样呢?
清单 32
// 添加jdbcRS. JoinRowSet joinRS = new JoinRowSetImpl(); jdbcRS.setMatchColumn("ID"); //$NON-NLS-1$ joinRS.addRowSet(jdbcRS); // 在添加cachedRS之前,改变指针的位置。 cachedRS.last(); joinRS.addRowSet(cachedRS, "ID"); //$NON-NLS-1$ // 结果. 此时JoinRowSet中的数据。 printRowSet(joinRS); |
The data in CachedRowSet: 2 1 Compute Jim null 1 1 Book Tom Tom is VIP. |
|
本文介绍了
javax.sql.rowset包下五个 RowSet 接口的使用,并重点说明了在使用中可能出现的问题。合理利用 RowSet 提供的离线式数据处理功能可以达到事半功倍的效果。
相关文章推荐
- Java 6 RowSet 使用完全剖析(1)
- Java 6 RowSet 使用完全剖析
- [转]Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析(2)
- Java 6 RowSet 使用完全剖析(3)
- Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析(1)
- Java 6 RowSet 使用完全剖析(2)
- Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析(3)
- Java 6 RowSet 使用完全剖析
- Java 6 RowSet 使用完全剖析
- Java剖析工具JProfiler入门使用教程:与Eclipse 3.2 集成
- Java 中 Vector、ArrayList、List 使用深入剖析
- Java 中Iterator 、Vector、ArrayList、List 使用深入剖析
- Java 中 Vector、ArrayList、List 使用深入剖析
- Java 中 Vector、ArrayList、List 使用深入剖析