2. 概述

本文将深入探讨JDBC中的RowSet接口。与传统的ResultSet相比,RowSet对象以更灵活、更易用的方式持有表格数据。

Oracle为最常见的RowSet使用场景定义了五种实现:

  • JdbcRowSet
  • CachedRowSet
  • WebRowSet
  • JoinRowSet
  • FilteredRowSet

接下来我们将逐一解析这些接口的使用方法。

3. JdbcRowSet

先从JdbcRowSet开始。创建时只需设置数据库连接的URL、用户名和密码:

JdbcRowSet jdbcRS = RowSetProvider.newFactory().createJdbcRowSet();
jdbcRS.setUrl("jdbc:h2:mem:testdb");
jdbcRS.setUsername("sa");
jdbcRS.setPassword("");
jdbcRS.setType(ResultSet.TYPE_SCROLL_INSENSITIVE);
String sql = "SELECT * FROM customers";
jdbcRS.setCommand(sql);
jdbcRS.execute();
jdbcRS.addRowSetListener(new ExampleListener());
while (jdbcRS.next()) {
    // 每次调用next都会触发cursorMoved事件
    System.out.println("id = " + jdbcRS.getString(1));
    System.out.println("name = " + jdbcRS.getString(2));
}

⚠️ 注意:在调用setCommand方法定义SQL语句并执行execute方法前,jdbcRS对象中是没有数据的。

另外,为了处理事件,我们给JdbcRowSet添加了RowSetListener监听器。

JdbcRowSet与其他四种实现的核心区别在于:它始终与数据库保持连接,因此与ResultSet对象最为相似。

4. CachedRowSet

CachedRowSet的独特之处在于它可以在不连接数据源的情况下工作,我们称之为"离线RowSet对象"。

它得名于其将数据缓存在内存中的特性,使其能直接操作内存数据而非数据库数据。

由于CachedRowSet所有离线RowSet对象的超接口,以下代码同样适用于WebRowSetJoinRowSetFilteredRowSet

CachedRowSet crs = RowSetProvider.newFactory().createCachedRowSet();
crs.setUsername(username);
crs.setPassword(password);
crs.setUrl(url);
crs.setCommand(sql);
crs.execute();
crs.addRowSetListener(new ExampleListener());
while (crs.next()) {
    if (crs.getInt("id") == 1) {
        System.out.println("CRS找到customer1记录,将删除该行");
        crs.deleteRow();
        break;
    }
}

5. WebRowSet

接下来看WebRowSet。它的独特之处在于:除了具备CachedRowSet的所有功能外,还能将自身写入XML文档,并从XML文档读取数据恢复为WebRowSet对象

WebRowSet wrs = RowSetProvider.newFactory().createWebRowSet();
wrs.setUsername(username);
wrs.setPassword(password);
wrs.setUrl(url);
wrs.setCommand(sql);
wrs.execute();
FileOutputStream ostream = new FileOutputStream("customers.xml");
wrs.writeXml(ostream);

通过writeXml方法,我们将WebRowSet对象的当前状态写入XML文档。传入OutputStream对象可以字节方式写入,这对处理各类数据特别实用。

6. JoinRowSet

JoinRowSet允许我们在内存中对多个RowSet对象执行SQL JOIN操作,无需创建数据库连接,省去了连接开销:

CachedRowSet customers = RowSetProvider.newFactory().createCachedRowSet();
// 配置customers的CachedRowSet设置
CachedRowSetImpl associates = new CachedRowSetImpl();
// 配置associates的CachedRowSet设置            
JoinRowSet jrs = new JoinRowSetImpl();
jrs.addRowSet(customers, "ID");
jrs.addRowSet(associates, "ID");

每个加入JoinRowSet的对象都需要指定匹配列(即SQL JOIN的依据列),我们在addRowSet方法中指定了"id"列。

✅ 提示:除了使用列名,也可以使用列号(如addRowSet(customers, 1))。

7. FilteredRowSet

最后是FilteredRowSet,它能过滤RowSet中可见的行数,让我们只关注当前任务相关的数据。

通过实现Predicate接口来定义过滤逻辑:

public class FilterExample implements Predicate {
    
    private Pattern pattern;
    
    public FilterExample(String regexQuery) {
        if (regexQuery != null && !regexQuery.isEmpty()) {
            pattern = Pattern.compile(regexQuery);
        }
    }
 
    public boolean evaluate(RowSet rs) {
        try {
            if (!rs.isAfterLast()) {
                String name = rs.getString("name");
                System.out.println(String.format(
                  "正在用模式'%s'匹配 %s", pattern.toString(),
                  name));
                Matcher matcher = pattern.matcher(name);
                return matcher.matches();
            } else
                return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    // 错误处理方法
}

将过滤器应用到FilteredRowSet对象:

RowSetFactory rsf = RowSetProvider.newFactory();
FilteredRowSet frs = rsf.createFilteredRowSet();
frs.setCommand("select * from customers");
frs.execute(conn);
frs.setFilter(new FilterExample("^[A-C].*"));
            
ResultSetMetaData rsmd = frs.getMetaData();
int columncount = rsmd.getColumnCount();
while (frs.next()) {
    for (int i = 1; i <= columncount; i++) {
        System.out.println(
          rsmd.getColumnLabel(i)
          + " = "
          + frs.getObject(i) + " ");
        }
    }

8. 总结

本文快速解析了JDK中五种标准的RowSet接口实现:

  • JdbcRowSet唯一保持连接的实现
  • CachedRowSet:基础离线实现
  • WebRowSet:支持XML序列化
  • JoinRowSet:内存JOIN操作
  • FilteredRowSet:数据行过滤

所有代码示例可在GitHub仓库获取完整版。


原始标题:Introduction to the JDBC RowSet Interface in Java | Baeldung