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

分页

由于 CachedRowSet 是将数据临时存储在内存中,因此对于许多 SQL 查询,会返回大量的数据。如果将整个结果集全部存储在内存中会占用大量的内存,有时甚至是不可行的。对此 CachedRowSet 提供了分批从 ResultSet 中获取数据的方式,这就是分页。应用程序可以简单的通过 setPageSize 设置一页中数据的最大行数。也就是说,如果页大小设置为 5,一次只会从数据源获取 5 条数据。下面的代码示范了如何进行简单分页操作。(分页部分代码默认 ORDERS 表中有 10 条数据)

 

清单 11. 分页代码一

Java代码   收藏代码
  1. ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS);   
  2.   
  3. CachedRowSet cachedRS = new CachedRowSetImpl(); // 设置页大小   
  4.   
  5. cachedRS.setPageSize(4);   
  6.   
  7. cachedRS.populate(rs, 1);   
  8.   
  9. while (cachedRS.nextPage()) { printRowSet(cachedRS); }   
  10.   
  11. 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. 分页代码二

Java代码   收藏代码
  1. ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS);   
  2.   
  3. CachedRowSet cachedRS = new CachedRowSetImpl(); // 设置页大小   
  4.   
  5. cachedRS.setPageSize(4);   
  6.   
  7. cachedRS.populate(rs, 1);   
  8.   
  9. printRowSet(cachedRS);   
  10.   
  11. while (cachedRS.nextPage()) {   
  12.     printRowSet(cachedRS);   
  13. }    

 


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

setMaxRows 可以设置 CachedRowSet 可遍历的最大行数。下面是 setMaxRows 和 setPageSize 同时使用的例子。

 

清单 13. 分页代码三

Java代码   收藏代码
  1. ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS);   
  2.   
  3. CachedRowSet cachedRS = new CachedRowSetImpl();   
  4.   
  5. cachedRS.setPageSize(4);   
  6.   
  7. cachedRS.setMaxRows(7);   
  8.   
  9. cachedRS.populate(rs, 1);   
  10.   
  11. while (cachedRS.nextPage()) { printRowSet(cachedRS); }   
  12.   
  13. 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. 分页代码四

Java代码   收藏代码
  1. ResultSet rs = stmt.executeQuery(DBCreator.SQL_SELECT_ORDERS);   
  2.   
  3. CachedRowSet cachedRS = new CachedRowSetImpl();   
  4.   
  5. cachedRS.setPageSize(4);   
  6.   
  7. cachedRS.populate(rs, 1);   
  8.   
  9. cachedRS.setPageSize(3);   
  10.   
  11. while (cachedRS.nextPage()) { printRowSet(cachedRS); }   
  12.   
  13. 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.

Java代码   收藏代码
  1. cachedRS.absolute(2);   
  2.   
  3. cachedRS.deleteRow();   
  4.   
  5. cachedRS.acceptChanges();    

 

使用 WebRowSet

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

 

清单 18. 读取 XML 文件

Java代码   收藏代码
  1. WebRowSet newWebRS = new WebRowSetImpl();   
  2.   
  3. newWebRS.readXml(new FileReader("CUSTOMERS.xml"));   

  

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

 

清单 19. 生成 XML 文件

Java代码   收藏代码
  1. WebRowSet webRS = new WebRowSetImpl();   
  2.   
  3. CachedRowSetDemo.fillRowSetWithExecute(webRS); // 输出到XML文件   
  4.   
  5. FileWriter fileWriter = new FileWriter("CUSTOMERS.xml");   
  6.   
  7. 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

Java代码   收藏代码
  1. class Range implements Predicate   
  2. {   
  3.     @Override public boolean evaluate(RowSet rs)   
  4.     {   
  5.          try {   
  6.                    if (rs.getInt(1) > 1)   
  7.                        {   
  8.                             return true;   
  9.                        }   
  10.               } catch (SQLException e) { // do nothing }   
  11.         return false;   
  12. }   
  13.   
  14.     @Override public boolean evaluate(Object value, int column) throws SQLException { return false; }   
  15.   
  16.     @Override public boolean evaluate(Object value, String columnName) throws SQLException   
  17.     { return false; }   
  18. }   

 

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

 

清单 21.

 

Java代码   收藏代码
  1. FilteredRowSet filterRS = new FilteredRowSetImpl();   
  2. CachedRowSetDemo.fillRowSetWithExecute(filterRS);   
  3. System.out.println("/*******Before set filter***********/");   
  4. CachedRowSetDemo.printRowSet(filterRS);   
  5. System.out.println("\n/*******After set filter***********/");   
  6. Range range = new Range(); filterRS.setFilter(range);   
  7. 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.

Java代码   收藏代码
  1. JdbcRowSet JdbcRowSet jrs = new JdbcRowSetImpl();   
  2. jrs.setCommand(DBCreator.SQL_SELECT_CUSTOMERS);   
  3. jrs.setUrl(DBCreator.DERBY_URL);   
  4. jrs.execute();   
  5. while (jrs.next())   
  6. {   
  7.     for(int i = 1; i <= jrs.getMetaData().getColumnCount(); i++)   
  8.    {   
  9.        System.out.print(jrs.getObject(i) + " ");   
  10.    }   
  11.    System.out.println();   
  12. }    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值