Java 6 RowSet 使用完全剖析(2)

分页

由于 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); }  

 


可以看到只需要在 populate 之前使用 setPageSize 设置页的大小,就可以轻松实现分页了。每次调用 nextPage 或 previousPage 进行翻页后,行游标都会被自动移动到当前页第一行的前面,并且只能在当前页内移动。这样我们对每一页都可以像新的数据集一样进行遍历,非常方便。这里需要注意的是:

用来填充 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); 
}  

 


我们看到第一页被输出了两次。也就是说使用 next 始终是可以遍历第一页的,而每次 nextPage 后,行游标都会被重置到当前页的第一行数据之前。对于使用 execute 填充数据的方式,通过 setPageSize 同样可以实现分页。

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();  

 

上面的例子中,分别将页大小设置为 4,最大行数设置为 7(设置页大小时,如果最大行数不等于 0,就必须小于等于最大行数),然后遍历打印所有行,输出如下。

清单 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 


我们注意到,虽然满足 Select 条件的数据有 10 条,但由于我们使用 setMaxRows 设置了允许的最大行数,所以最终我们只得到了 7 条数据。另外 setPageSize 只会改变下一次填充页时的大小,无法影响当前页的大小。我们可以在数据填充完毕后再改变页的大小。

 

清单 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();

  


我们在 populate 数据之前设置的页大小是 4,在 populate 数据之后又将页大小设置为 3,然后遍历输出结果。

 

清单 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 Perl The data in RowSet: 8 1 Ruby 9 1 Erlang 10 2 Python 


我们发现除了第一页有 4 条数据外,其余页都只有 3 条数据。实际上 populate 中就已经对第一页数据进行了填充,并且使用之前设置的 4 作为页大小。所以 populate 之后设置的页大小就只能从第二页开始起作用了。setMaxSize 与此相似,也是在每次填充页时计算。

应注意的问题

在 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

WebRowSet 继承于 CachedRowSet,因此用来填充 CachedRowSet 的方式同样适用于 WebRowSet。WebRowSet 也可以读取一个符合规范的 XML 文件,填充自己。假定 CUSTOMERS.xml 是一个符合规范的 XML 文件,里面存放的是表 CUSTOMERS 的数据。

 

清单 18. 读取 XML 文件

WebRowSet newWebRS = new WebRowSetImpl(); 

newWebRS.readXml(new FileReader("CUSTOMERS.xml")); 

  

相比于 CachedRowSet,WebRowSet 就是添加了对 XML 文件读写的支持。它可以将自身数据输出到 XML 文件,也可以读取符合规范的 XML 文件,来填充自己。上段示例代码中已经演示了如何读取 XML 文件。如下示例代码则是生成一个 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

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; } 
} 

 

下面这段代码演示了使用上面定义的 class Range 前后的数据变化。

 

清单 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);  

 
清单 22. 清单 21 中的代码执行结果

/*******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 

 

可以看出,在设置了过滤器后,再次打印 FilteredRowSet 中的数据时,由于第一行第一列的值为 1,不符合过滤规则,因此,没有打印出来。上面实现的这个 Predicate 中,用来检测新插入行是否符合过滤规则的方法直接返回 false。这样,在新插入行时,无论插入什么值时,都将抛出 SQLException。

 

应注意的问题

当设置了过滤器后,FilteredRowSet.absolute(1) 无论何时都返回 false。按照规范,如果第一行值不符合过滤规则,则移到第二行,依次类推,直到找到第一条符合过滤规则的结果行并返回 true,否则,返回 false。
在 JDK 5.0 下,如果 FilteredRowSet 没有设置过滤器,那么在新插入一行时会抛出 NullPointerException。在 JDK 6.0 中,已解决该问题。
使用 JdbcRowSet

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) + " "); 
   } 
   System.out.println(); 
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值