13.6 Java7的RowSet1.1 13.6.1 Java 7新增的RowSetFactory与RowSet

13.6 Java7的RowSet1.1

RowSet接口继承了ResultSet接口, RowSet接口下包含JdbcRowSetCachedRowSetFilteredRowSetJoinRowsetWebRowSet常用子接口。除了JdbcRowSet需要保持与数据库的连接之外,其余4个子接口都是离线的RowSet,无须保持与数据库的连接。
ResultSet相比, RowSet默认是可滚动可更新可序列化的结果集,而且作为JavaBean使用,因此能方便地在网络上传输,用于同步两端的数据。对于离线RowSet而言,程序在创建RowSet时已把数据从底层数据库读取到了内存,因此可以充分利用计算机的内存,从而降低数据库服务器的负载,提高程序性能。
图13.22显示了RowSet规范的接口类图。
这里有一张图片
CachedRowSet及其子接口都代表了离线RowSet,它们都不需要底层数据库连接。

13.6.1 Java 7新增的RowSetFactory与RowSet

Java 6.0以前, RowSet及其5个子接口都已经存在了,但在实际编程中的应用一直并不广泛,这是因为Java公开API并没有为RowSet及其各子接口提供实现类,而且也没有提供公开的方法来创建RowSet及其各子接口的实例。
实际上,Java6.0已经在com.sun.rowset包下提供了JdbcRowSetlmplCachedRowSetlmplWebRowSetlmplFilteredRowSetlmplJoinRowSetlmpl五个实现类,它们代表了各种RowSet接口的实现类
JdbcRowSet为例,在Java 6.0及以前的版本中,如果程序需要使用JdbcRowSet,则必须通过调用JdbcRowSetlmpl的构造器来创建JdbcRowSet实例, JdbcRowSetlmpl提供了如下常用构造器.

方法 描述
JdbcRowSetlmpl() 创建一个默认的JdbcRowSetlmpl对象。
JdbcRowSetImpl(Connection conn) 以给定的Connection对象作为数据库连接来创建JdbcRowSetlmpl对象。
JdbcRowSetlmpl(ResultSet rs) 创建包装ResultSet对象的JdbcRowSetlmpl对象.

除此之外, RowSet接口中定义了如下常用方法。

方法 描述
setUrl(String ur) 设置该 RowSet要访问的数据库的URL
setUsername(String name) 设置该RowSet要访问的数据库的用户名。
setPassword(String password) 设置该RowSet要访问的数据库的密码。
setCommand(String sql) 设置使用该sql语句的查询结果来装填该RowSet
execute() 执行查询
populate(ResultSet rs) 让该RowSet直接包装给定的 ResultSet对象.
通过JdbcRowSet的构造器和上面几个方法不难看出,为JdbcRowSet装填数据有如下两种方式。
  • 1.创建JdbcRowSetlmpl时,直接传入ResultSet对象。
  • 2.使用execute()方法来执行SQL査询,用查询返回的数据来装填RowSet.

对于第二种方式来说,如果创建JdbcRowSetlmpl时已经传入了Connection参数,则只要先调用setCommand(String sql)指定SQL查询语句,接下来就可调用execute()方法执行查询了。如果创建JdbcRowSetlmpl时没有传入Connection参数,则先要为JdbcRowSet设置数据库的URL、用户名、密码等连接信息
下面程序通过JdbcRowSetlmpl示范了使用JdbcRowSet可滚动可修改特性.

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
import java.util.*;
import java.io.*;
import java.sql.*;
import javax.sql.rowset.*;
import com.sun.rowset.*;

public class JdbcRowSetTest
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception
{
// 使用Properties类来加载属性文件
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 void update(String sql) throws Exception
{
// .加载驱动
Class.forName(driver);
try (
// 2.获取数据库连接
Connection conn = DriverManager.getConnection(url, user, pass);
// 3.创建JdbcRowSetImpl对象
JdbcRowSet jdbcRs = new JdbcRowSetImpl(conn)) // ①
{
// 设置SQL查询语句
jdbcRs.setCommand(sql);
// 执行查询
jdbcRs.execute();
// 记录指针移动到最后一行
jdbcRs.afterLast();
// 向前滚动结果集
while (jdbcRs.previous())
{
System.out.println(jdbcRs.getString(1) + "\t"
+ jdbcRs.getString(2) + "\t" + jdbcRs.getString(3));
if (jdbcRs.getInt("student_id") == 3)
{
// 修改指定记录行
jdbcRs.updateString("student_name", "孙悟空");
// 更新该行记录
jdbcRs.updateRow();
}
}
}
}
public static void main(String[] args) throws Exception
{
JdbcRowSetTest jt = new JdbcRowSetTest();
jt.initParam("mysql.ini");
jt.update("select * from student_table");
}
}

上面程序中①号代码创建一个JdbcRowSetlmpl实例,这就是一个JdbcRowSet对象。接下来的代码则示范了JdbcSet可滚动可修改的特性。
编译该程序,编译器将会在①号代码处发出警告: JdbcRowSetlmpl是内部专用AP,可能会在未来发行版中删除。这就是直接使用JdbcRowSetImpl的代价.
需要说明的是,使用JdbcRowSetlmpl除了编译器会发出警告之外,还有如下坏处。

  • 程序直接与JdbcRowSetlmpl实现类耦合,不利于后期的升级、扩展。
  • JdbcRowSetlmpl实现类不是一个公开的API,未来可能被删除。

正是因为上面两个原因,所以在Java 6.0时代, RowSet并未得到广泛应用。Java 7新增了RowSetProvider类和Row SetFactory接口,其中RowSetProvider负责创建RowSetFactory,而RowSetFactory则提供了如下方法来创建RowSet实例。

方法 描述
CachedRowSet createCachedRowSet() 创建一个默认的CachedRowSet
FilteredRowSet createFilteredRowSet() 创建一个默认的FilteredRowSet
JdbcRowSet createJdbcRowSet() 创建一个默认的JdbcRowSet
JoinRowSet createJoinRowSet() 创建一个默认的JoinRowSet
WebRowSet createWebRowSet() 创建一个默认的WebRowSet

通过使用RowSetFactory,就可以把应用程序与RowSet实现类分离开,避免直接使用JdbcRowSetlmpl等非公开的API,也更有利于后期的升级、扩展。
下面程序使用RowSetFactory来创建JdbcRowSet实例。

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
import java.util.*;
import java.io.*;
import java.sql.*;
import javax.sql.rowset.*;

public class RowSetFactoryTest
{
private String driver;
private String url;
private String user;
private String pass;
public void initParam(String paramFile) throws Exception
{
// 使用Properties类来加载属性文件
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 void update(String sql) throws Exception
{
// 1.加载驱动
Class.forName(driver);
// 2.使用RowSetProvider创建RowSetFactory
RowSetFactory factory = RowSetProvider.newFactory();
try (
// 3.使用RowSetFactory创建默认的JdbcRowSet实例
JdbcRowSet jdbcRowSet = factory.createJdbcRowSet())
{
// 4.设置必要的连接信息
jdbcRowSet.setUrl(url);
jdbcRowSet.setUsername(user);
jdbcRowSet.setPassword(pass);
// 设置SQL查询语句
jdbcRowSet.setCommand(sql);
// 执行查询
jdbcRowSet.execute();
jdbcRowSet.afterLast();
// 向前滚动结果集
while (jdbcRowSet.previous())
{
System.out.println(jdbcRowSet.getString(1) + "\t"
+ jdbcRowSet.getString(2) + "\t" + jdbcRowSet.getString(3));
if (jdbcRowSet.getInt("student_id") == 3)
{
// 修改指定记录行
jdbcRowSet.updateString("student_name", "孙悟空");
jdbcRowSet.updateRow();
}
}
}
}
public static void main(String[] args) throws Exception
{
RowSetFactoryTest jt = new RowSetFactoryTest();
jt.initParam("mysql.ini");
jt.update("select * from student_table");
}
}

上面程序中使用了RowSetFactory来创建JdbcRowSet对象,这就避免了与JabcRowSetlmpl实现类耦合。由于通过这种方式创建的JdbcRowSet还没有传入Connection参数,因此程序还需调用setUrl()setUsername()setPassword()等方法来设置数据库连接信息编译、运行该程序,编译器不会发出任何警告,程序运行结果也一切正常。