利用 DB2 UDB 来使用 Java CachedRowSet 实现
断开连接
将此页作为电子邮件发送
未显示需要 JavaScript 的文档选项
样例代码
级别: 初级
Kulvir Singh Bhogal (kbhogal@us.ibm.com), IBM Software Services for WebSphere, IBM Corporation
2004 年 8 月 01 日
高速缓存行集(Cached Row Set)是 Java™ 1.5 提供的一项新功能,源自 JSR114 的努力。这一新功能使您可以拥有一个可串行化的断开连接的对象。这意味着您可以连接到数据库,以结果集的形式取得数据,释放连接并在本地操纵这些数据,然后恢复连接以完成事务,这样可以大大减少对连接和服务器资源的使用。本文展示这一切是如何利用 DB2® Universal Database™ 来实现的,并包含示例代码。
简介
应该把数据库连接看作是一种被应用程序广泛使用的宝贵资源。尤其是在高流量的 Web 站点中,客户机应该使用由应用程序服务器(例如,IBM WebSphere® Application Server)管理的数据库连接池中的数据库连接,并在其必要的事务发生之后释放数据库连接,这一点很重要。
在许多情况下,事务会话会持续较长一段时间,占用着数据库连接,直到完成。过去,Java 程序员一直使用没有高速缓存解决方案的 JDBC API。高速缓存解决方案允许用户在本地高速缓存结果集,释放连接,操纵结果集数据,并在稍后的时间点与数据库同步。在本文中,我将介绍 javax.sql.RowSet 接口的实现,该实现允许我们实现高速缓存解决方案。
回页首
获得 RowSet 实现
javax.sql.RowSet 接口是在 J2SE Version 1.4 中引入到 Java 语言的。随着 Java 2 Standard Edition version 1.5 的发布,我们将会看到该接口的增强和绑定到语言的接口实现。 RowSet 接口是 JSR 114的结果,JSR 114 勾勒了一个方案,也就是“将表格数据与数据源分隔开……”,从而增加“应用程序的可扩展性和编程模型的灵活性”。我使用 beta 版本的 Tiger (Java 1.5) 来进行开发。如果您使用的是以前版本的 Java,应该尝试 RowSet 的参考实现,可以 从 Sun Microsystems 下载参考实现。对于我们的例子,我们将利用 CachedRowSet 实现。
回页首
您以前看到过 RowSet 实现
本文中突出展示的 CachedRowSetImpl 类是 javax.sql.Rowset 接口的一个实现。 javax.sql.Rowset 扩展了 java.sql.ResultSet 接口。因此,如果您熟悉 javax.sql.ResultSet 接口,我是这样假设的,那么您会认出 CachedRowSetImpl 提供的许多方法。 java.sql.RowSet 接口提供您在标准 JDBC 2.0 ResultSet 中看到过的所有方法;附加价值是,我们不需要连续使用数据库连接。就相当于我们可以走进商店,取走商品目录之后进行挑选,然后再带着填好的订单回到商店。
回页首
使用 CachedRowSet
CachedRowSet 是一个 JavaBean。您可以通过使用现有的 ResultSet 对象,或者通过指定连接连接信息和 SQL 查询,来填充 CachedRowSet 。我们采用后一种方法。但是,首先为我们的沙箱创建一个小的 DB2 数据库。
db2 => create database cachedex
我假设在您的机器上具有一个叫做 db2admin 的用户,口令为 db2admin:
db2=> connect to cachedex user db2admin using db2admin
创建一个名为 cachetbl 的表:
db2=> create table cachetbl (id int primary key not null, firstname varchar(40) not null, lastname varchar(40) not null)
然后用以下行填充该表:
insert into cachetbl values (12345,’Kulvir’,’Bhogal’)
insert into cachetbl values (23456,’Meet’,’Feona’)
insert into cachetbl values (34567,’Bicky’,’Singh’)
我提供的示例代码建立一个到 DB2 的直接连接。如果您是通过由应用程序服务器(比如 IBM WebSphere Application Server)管理的池获得数据库连接,那么可以使用 execute 方法的另一种形式,该方法利用 java.sql.Connection 对象作为参数。通过阅读我的相关主题的文章 Using Data Sources the Right Way,可以更多地了解使用数据源 —— 一种 J2EE 最佳实践。
回页首
可串行化
javax.sql.RowSet 的实现可以被串行化。对于处理 Enterprise Java Beans 的程序员来说,这是一个比较好的消息。标准 JDBC 2.0 ResultSets 是不可串行化的,这使得是否需要使用定制对象,以便 ResultSet 数据可以被发送回 EJB 设置中的客户机以进行操纵或查询,成了一个争论。有了 RowSet 实现的出现,我们就可以串行化 ResultSets ,将它们发送到我们的客户机,然后我们的客户机可以读取和更新 ResultSet ,并将它发送回服务器。
回页首
可滚动
CachedRowSetImpl 实现是可滚动的,所以允许您在 RowSet 给出的记录集中向前和向后滚动。这在 JDBC 2.0 ResultSet 中是允许的。但是以前,在滚动发生期间必须维护一个会话。现在,我们可以在离线数据中滚动了。
回页首
作一个数据完整性的乐天派
您可能会想,如果在客户机 A 将更改与数据库服务器进行同步之前,客户机 A 上缓存的数据被另一个客户机(客户机 B)操纵,那将会发生什么事情。 CachedRowSet 的默认实现不对数据库服务器维护锁。参考实现使用乐观的同步。具体地说就是,如果客户机 A 试图操纵的数据在数据库服务器上没有更改,那么更新将会被数据库接受。但是,如果在次期间目标数据被更改,就会抛出一个同步异常。注意,这种乐观方法也是参考实现处理并发性的方式。其他实现可能采取不同的并发策略;规范并不强制要求使用某个特定的并发模型。
回页首
查看起作用的东西
我提供了一个 示例程序,该程序演示了 CachedRowSetImpl 提供的一些功能。这些代码都可以在文件 DisconnectedExample.java 中找到。我将解释该程序的一些部分,以便您可以理解我想要传达的内容。注意,要运行示例应用程序,需要在运行时类路径中包含 DB2 Universal JDBC 驱动程序 (db2jcc.jar)。还需要包含 db2jcc_license_cu.jar 文件。关于设置环境的更多信息,请参考我的文章: Hooking Up with DB2 Universal Database Version 8 using Java。
Class.forName("com.ibm.db2.jcc.DB2Driver");
CachedRowSet crs = new CachedRowSetImpl();
crs.setUsername("db2admin"); crs.setPassword("db2admin"); crs.setUrl("jdbc:db2://localhost:50000/cachedex"); crs.setCommand("SELECT id,firstname,lastname from cachetbl");
crs.execute();
System.out.println("---------------------------");
// display size of cached row set
System.out.println("Size: " + crs.size() + " records");
// display records in cachedrowset
while (crs.next())
{
System.out.println(crs.getRow() + " - " + crs.getString("firstname") + " " + crs.getString("lastname"));
}
System.out.println("---------------------------");
在上面的代码中,我创建了一个 com.sun.rowset.CachedRowSetImpl 对象。注意我是如何为 CachedRowsetImpl 对象指定数据库连接信息的。我用 setCommand 方法来指定想要执行的查询。当发出 execute 方法时,就会填充 CachedRowSetImpl 对象。就是在该方法调用中,我们获得然后再关闭数据库连接。然后我可以在对象中迭代,并使用 getter 方法来抽取和报告其中包含的数据。
正如前面所提到的, CachedRowSetImpl 对象是可滚动的。下面的代码例示了这项功能,我在该代码中使用 last() 和 previous() 方法在数据间前后滚动。
System.out.println("Showcase scrollability");
// move backwards through rowset
// scroll to last row
crs.last();
// iterate to next row
while (crs.previous())
{
// report current row contents
System.out.println(crs.getRow() + " - " + crs.getString("lastname"));
}
方法 first() 和 next() 也可用于在数据间滚动。
为了演示从数据库断开连接的能力,示例程序设计为暂停,并让您实际地执行生动的操作,即停止 DB2,以真正了解 CachedRowSet 功能的优势。在继续操作(由完成 BufferedReader 对象的一个简单 readLine 而推动)之前,应用程序等待按下任意键。当示例程序提示您停止 DB2 时,可以在一个新的 DB2 命令行处理器窗口中使用下面的命令:
db2 force applications all
然后再使用
db2stop
来强迫 DB2 的停止。
尽管数据库已经停止,但是下面的代码必须执行:
System.out.println("Demonstration of how to update cached row set"); crs.updateString("lastname","Kaur");
// commit changes to cached portion of rowset
crs.updateRow();
System.out.println(crs.getRow() + " - " + crs.getString("lastname"));
在上面的代码中,我们更改了当前行条目的 last name 字段。注意,对 CachedRowSetImpl 对象使用了 updateRow() 方法,以记录对该对象的更改。在这一时间点,数据库还没有被更新。正如您可以回想起的,数据库 甚至没有运行。应用程序将提示您启动 DB2。为此,可以从 DB2 命令行处理器发出下面的命令:
db2start
在 DB2 启动之后,按任意键继续示例应用程序。
此时,下面的代码将会运行,这将有效地在数据库中同步更改。
crs.acceptChanges();
通过对 cachetbl 执行一个选择查询,可以确认数据库实际上已经更新,如 图 1所示:
图 1. 确认更新更改已经与数据库同步
为了结束我们对 CachedRowSet 的动手研究,示例应用程序还演示了如何插入和删除行。要执行插入,必须将光标移动到一个叫做“插入行”的特殊位置。在这里,我们使用更新方法来填充新行。当我们使用 insertRow() 方法时, CachedRowSetImpl 对象就会被更新。当执行 acceptChanges() 方法时,更改就被持久地存储到数据库中。
以下代码演示了这一点:
// move the cursor to a blank row crs.moveToInsertRow();
// populate the new row
crs.updateInt(1,01234);
crs.updateString(2,"Judith");
crs.updateString(3,"Smith");
// insert the new row
crs.insertRow();
// move cursor back to previous position
crs.moveToCurrentRow();
// synchronize changes to database
crs.acceptChanges();
在插入完成之后程序将会暂停,以允许您在数据库中查询插入。
从对象删除行相当直观。只需要将光标定位到想要删除的地方,然后使用 deleteRow 方法即可。跟前面一样,需要一个后续的同步,以将更改持久存储到数据库中:
// delete row (where the cursor is currently positioned)
crs.deleteRow();
// synchronize changes to database
crs.acceptChanges();
回页首
只是一种实现
读者应该注意, javax.sql.RowSet 接口的 CachedRowSetImpl 实现只是一种实现(更确切地说是参考实现)。其他供应商也拥有 RowSet 的实现,这些实现与参考实现处理问题(比如数据完整性)的方式可能会不同(例如,第三方实现可能会对服务器进行锁维护)。
回页首
普遍含义
可更新的“断开连接的” ResultSets 在普遍世界中具有各种含义,其中,网络连接可以是断断续续的。使用 CachedRowSet 可以允许客户机在连接可用时将数据缓存在本地,然后重新连接,以同步对数据执行的更改。
回页首
谨慎使用
断开连接具有其优点。但是用户必须理解,断开连接的对象是保存在内存中。因此,不应该使用具有大型结果集的方法。大型结果集的相应更新和滚动会是耗资源的操作。
回页首
结束语
与标准 JDBC 2.0 ResultSet 不一样,利用 CachedRowSet 不再需要连续使用数据库连接。因为数据库连接池中的数据库连接是宝贵的资源,所以连接数据库、断开连接,然后重新连接以便同步数据库,这种能力无疑是非常受欢迎的。
回页首
致谢
作者感谢 IBM Life Sciences Development 的 Richard Dettinger 对本文的审稿。
回页首
下载
名字 大小 下载方法
DisconnectedExample.java
4.03 KB
HTTP
关于下载方法的信息
Get Adobe® Reader®
参考资料
关于作者
Kulvir Singh Bhogal 是一名 IBM 顾问,他为全国性的客户网站设计和实现以 Java 为中心的解决方案。 .
源文档 <http://www.ibm.com/developerworks/cn/db2/library/techarticles/dm-0406bhogal/index.html>