Linux、Unix 和 Windows 平台上的 DB2 Universal Database (UDB) version 8.2 支持很多类型的 Java 程序,包括在客户机应用程序、应用程序服务器和 DB2 UDB 服务器中使用的存储过程或用户定义函数这两种形式的程序。
IBM 引入了 DB2 Universal JDBC Driver,以支持 DB2 UDB v.8.1 中的 JDBC 和 SQLJ (用于 Java 应用程序的嵌入式 SQL)编程技术。在 DB2 UDB v.8.2 中,该驱动程序包括对分布式事务、JDBC 3.0 API 以及特定于驱动程序的可服务性和性能增强的完全支持。
DB2 v.8.2 中新的 JDBC 特性包括增强的结果集处理(可保持游标支持)和保存点(savepoint)。在 v.8.2 中,Java 应用程序监控得到很大程度的简化,因为有了新的可以获得 DB2 UDB for Linux、Unix 和 Windows 服务器性能信息的 SQL 函数。
我们将向您展示如何开始使用这种新的 JDBC 驱动程序,如何在 Java 应用程序中获得 DB2 UDB 服务器信息特征,以及如何在开放源代码开发环境 Eclipse 中构建用于 DB2 的 Java 应用程序。
DB2 UDB v.8.2 包括对很多新 JDBC 3.0 特性的支持,我们无法在此一一阐述。要了解关于 DB2 UDB v.8.2 的 Java 支持的更多信息,请参阅第 32 页的“Do More With More”以及第 51 页的 Resources。
Universal JDBC Driver
最新的 Java 编程规范是由 Java 2 Platform Enterprise Edition (J2EE) 1.4 定义的,该规范要求通过一个遵从 JDBC 3.0 的驱动程序来在各种基于 Java 的程序模块中访问 RDBMS。这些模块可能在客户机工作站上,或者在应用程序服务器中。
DB2 Universal JDBC 驱动程序包括对 Type 4 和 Type 2 模式的连接的支持。在选择一种 DB2 JDBC 连接模式之前,需要分析开发和运行环境。大多数用户偏爱“Type 4”驱动程序,因为它可以直接与应用程序一起提供,而不必依赖于共享库。Type 2 驱动程序总是需要一个共享库,因为驱动程序和服务器之间的通信是由该驱动程序的非 Java 组件来处理的。根据经验,如果要经过 TCP/IP 远程访问数据库,那么应该使用 Type 4;如果与 DB2 服务器(IPC)在同一个逻辑操作系统中,或者在一个 Java 存储过程中(不能使用 TCP/IP 来访问 DB2),则应该使用 Type 2。
通过 Java 建立连接
当与 Linux、Unix 和 Windows 上的 DB2 UDB 一起使用 DB2 Universal JDBC Driver 时,必须确保在 DB2 UDB 服务器上支持 TCP/IP 连接。为了确保 DB2 UDB 服务器实例可以接收来自 Java 应用程序的数据库访问请求,需要执行两个步骤:
1. db2set DB2COMM=TCPIP
2. db2 update dbm cfg using SVCENAME <tcp/ip service name>
在 DB2 UDB 服务器安装期间应该正确地设置这两项。但是,如果在建立到 DB2 UDB 服务器的初始连接时存在问题,那么应该首先检查这两项设置。SVCENAME 应该与 DB2 UDB 服务器所在计算机上服务文件中定义的端口相对应。
您也许知道,JDBC 是由 Java 2 Standard Edition (J2SE) 规范定义的 DB2 的一个动态 SQL 接口。Java 语言包括很多内建的类和接口,但并没有提供用于 DB2 UDB 服务器的 JDBC 驱动程序。因此,应用程序需要装载一组能够理解如何与 DB2 UDB 服务器交互的类和接口。下面这行 Java 代码将使用 DB2Driver 接口来完成这个任务:
Class.forName("com.ibm.db2.jcc.DB2Driver")
成功地装载了驱动程序之后,就可以使用 DriverManager.getConnection 方法连接到 DB2 UDB 服务器,该方法是通过嵌入在 URL 中的 Java 属性来配置的,该 URL 或者包括在 getConnection() 方法的参数中,或者在一个外部属性文件中提供。也可以不通过 DriverManager 来建立连接,而是使用可配置的 JDBC 2 数据源实例。
清单 1 展示了用于建立到数据库的连接的多种技术中的一种。用于连接到 DB2 的 URL 是 jdbc:db2://localhost:50000/sample。DB2 服务器主机名是 localhost,为通信定义的端口是 50000(由 DB2 UDB 服务器的实例配置中的 SVCENAME 定义)。这个连接 URL 表明,应用程序将连接到 SAMPLE 数据库。清单 1 中的例子要求将一个用户 ID 和密码传递给数据库服务器,以便进行认证。连接到数据库服务器之后,便执行显式地引用 GHUTCHIS.STAFF 表的 SQL 查询,并返回结果集。您可以使用列名或列的位置从 JDBC 结果中检索数据。
清单 1. 用 DB2 Universal JDBC Driver 建立连接
| import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class T4DB2Connect { public static void main(String[] args) { try { // load the DB2 Driver Class.forName("com.ibm.db2.jcc.DB2Driver"); // establish a connection to DB2 Connection db2Conn = DriverManager.getConnection( "jdbc:db2://localhost:50000/sample", "testuser", "testpassword"); Statement st = db2Conn.createStatement(); String query = "SELECT job, decimal(avg(salary),10,2) " + "FROM ghutchis.staff GROUP BY job"; // execute the query ResultSet resultSet = st.executeQuery(query); System.out.println("Job " + "\tAverage Salary"); System.out.println("-----" + "\t--------------"); while (resultSet.next()) { String jobType = resultSet.getString("job"); double avgSal = resultSet.getDouble(2); System.out.println(jobType + "\t" + avgSal); } resultSet.close(); st.close(); db2Conn.close(); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } catch (SQLException sqle) { sqle.printStackTrace(); } } } |
JDBC 驱动程序包含在 db2jcc.jar 文件中;相应的许可文件是 called db2jcc_license_xxx.jar。在这个例子中,我们将连接到一个 DB2 UDB for Windows 服务器,所以要使用 db2jcc_license_cu.jar 文件。如果您打算将应用程序连接到 DB2 UDB for z/OS,那么需要 db2jcc_license_cisuz.jar 文件,这个文件是随 DB2 UDB Connect 和 DB2 UDB Enterprise Server Edition 产品一起提供的。
还可以使用 Java Property 对象来通过 Universal JDBC 驱动程序连接到 DB2 服务器。使用属性文件(或数据源属性)的方法使得用于建立连接的细节与应用程序源代码分离,从而简化了新 DB2 服务器环境中应用程序的安装(见清单 2)。
清单 2. 使用 Property 对象连接到 DB2| import java.util.Properties; public class T4DB2ConnectProp { public static void main(String[] args) { try { // load the DB2 Driver Class.forName("com.ibm.db2.jcc.DB2Driver"); // establish a connection to DB2 Properties prop1 = new Properties(); prop1.put("user", "testuser"); prop1.put("password", "testpassword"); Connection db2Conn = DriverManager.getConnection( "jdbc:db2://localhost:50000/sample", prop1); |
用 Eclipse 进行开发
Eclipse 是一种易用的、开放源代码的集成开发环境(IDE),已成为很多 IBM 软件产品的基础,包括 Rational XDE for Java 和 WebSphere Studio Application Developer。我们将向您展示如何使用 Eclipse 2.1 来构建一个简单的用于 DB2 UDB v.8.2 的 JDBC 应用程序。
为了使用 Eclipse 来构建 DB2 Java 应用程序,首先要创建一个新的 Java 项目。图 1 展示了如何添加 DB2 Universal JDBC Driver 到项目环境中。
图 1. 添加 DB2 Universal JDBC Driver 到 Eclipse 中
图 2 展示了在 Eclipse Workbench 中执行的一个示例程序。这个查询的输出表明,销售代表的薪水要高于经理,而略低于总经理。
图 2. 在 Eclipse Workbench 中执行 T4DB2Connect 程序
图 3 展示了一个很好的 Eclipse 特性,该特性为 Java 应用程序开发提供了上下文敏感的帮助。对于 resultSet 对象,有很多不同的方法。在这个例子中,我们决定利用列偏移号来返回平均薪水,这是考虑到这个列是派生出来的,没有相关联的列名。
图 3. Eclipse 的帮助系统
JDBC Driver 特性
DB2 Universal JDBC Driver 包括很多可以在应用程序中考虑使用的特性。例如,currentSchema 属性可以为无限定 DB2 数据库对象提供一个模式限定词。
图 4 展示了如何使用模式限定词 GHUTCHIS 来限定对 EMPLOYEE 表的引用。您可以使用 clientApplicationInformation 属性来帮助识别 Java 应用程序中来自 DB2 服务器的模块。
图 4. 高级 Universal JDBC Driver 属性
DB2 UDB v.8.2 提供了一组新的 SQL 函数来获取 DB2 服务器性能信息(在 DB2 UDB v.8.2 的 SQL 参考中有相关文档)。清单 3 展示了如何收集关于 SQL 查询在 DB2 UDB 服务器的执行时间的信息。该特性使开发人员可以在开发期间确立查询性能的基准,而不必使用 DB2 管理工具或第三方的性能工具。
清单 3. 收集 SQL 查询在 DB2 服务器上的执行时间
| with t1 as (select * from table (snapshot_appl_info('SAMPLE',-1)) as snap_appl_info where tpmon_client_app = 'Payroll Module' ) select elapsed_exec_time_ms from table (snapshot_appl ('SAMPLE',-1)) as snap_appl where (select agent_id from t1) = snap_appl.agent_id ELAPSED_EXEC_TIME_MS -------------------- 77 |
新的 SQL 管理函数便于跟踪 Java 应用程序的执行细节。clientApplicationInformation 属性用于帮助确定用于获得 DB2 UDB 服务器上执行时间的应用程序模块。清单 3 获得标识为 Payroll Module 的应用程序的 agent_id,该应用程序连接到 SAMPLE 数据库。最后一条 SQL 语句的执行时间是 77 毫秒。
现在,让我们修改这段程序,使之获得在应用程序中的耗时和在 DB2 UDB 服务器上的执行时间(见清单 4)。下面是一个示例输出:
Query elapsed time from application: 30 milliseconds.
Total query execution time on DB2 server: 22 milliseconds.
| // Access the query execution time on DB2 UDB v.8.2 server query = "with t1 as " + "(select * from table (snapshot_appl_info('SAMPLE',-1)) as "+ "snap_appl_info where tpmon_client_app = 'Payroll Module') " + "select integer(elapsed_exec_time_ms) from table (snapshot_appl ('SAMPLE',-1)) as snap_appl " + "where (select agent_id from t1) = snap_appl.agent_id"; resultSet = st.executeQuery (query); while (resultSet.next()){ System.out.println("Total Query execution time on DB2 Stinger: " + resultSet.getInt(1) + " milliseconds."); } resultSet.close(); |
这些新的 SQL 函数使 Java 开发人员可以清楚地知道,对于任何查询,网络传输和查询执行各自所占的比重有多大。请注意,要执行快照 SQL 函数,用户必须有 DB2 实例上的 SYSCTRL (或更高)权限。
如果在 JDBC 驱动程序处理期间发生错误,则 DB2 Universal JDBC Driver 会提供基本的 SQL 异常信息以及 DB2 服务器错误细节。现在,让我们修改应用程序,使之包括打印特定于 DB2 的 DB2Diagnosable 对象的细节。我们还将通过将 currentSchema 设置为一个不存在的数据库对象来引入一个 SQL 错误:
prop1.put("currentSchema","FRED"); // Set unqualified references
图 5 展示了 DB2 SQLCA 数据结构。对于使用嵌入式 C 的程序员,这种数据结构可能非常面熟。消息标志指出无效的数据库对象引用(在这个例子中是 FRED.STAFF)。
图 5. DB2 Universal JDBC Driver 错误处理
JDBC 3.0
DB2 UDB v.8.2 引入了对 JDBC 3.0 规范的支持,包括新的结果集保持能力(holdability)和保存点。
可更新的结果集。Java 开发人员更喜欢从 Java API 的角度来思考,而不是从 SQL 的角度去思考。在 JDBC 1.0 中,表示查询结果的结果集有一个并发类型 CONCUR_READ_ONLY,这意味着结果集不能更新。而在 JDBC 2.0 中具有并发类型 CONCUR_UPDATABLE,这意味着可以使用 Java 代码、而不是 SQL 来更新结果集。通过 ResultSet 接口的各种 updateXX() 方法可以很方便地更新结果集,这些方法可以更新结果集的内容。程序员可以使用结果集中要更新的列的列索引,或者使用一个表示要更新的列的名称的字符串。
下面是 updateInt 方法的两种形式:
updateInt(String columnName, int newIntValue)
updateInt(int columnIndex, int newIntValue)
调用 updateXX 方法更改结果集之后,必须对 ResultSet 对象调用 updateRow 方法,以告知刚才的更新。清单 5 阐释了可更新结果集的概念。更早的 DB2 附带的 JDBC 驱动程序(即应用程序驱动程序 — Type 2 — 或网络驱动程序 — Type 3)要求显式地通过游标来使用结果集更新数据。清单 5 展示了更常见的(也是我们推荐的)用法。
| String sqlQuery = "select firstName, middleInitial, lastName, from applicants"; ResultSet resultSet = stmt.executeQuery(sqlQuery); while (resultSet.next()) { // update the middleInitial column of the applicants table with an "S" resultSet.updateStringt(2, "S."); // persist the changes of the result set to the database. resultSet.updateRow(); } |
事务保存点(transaction savepoint)。事务保存点使开发人员可以将更新成组地应用于数据库。这彻底改变了早期 JDBC 规范中的 all-or-nothing(要么全做,要么全不做)方法。事务保存点表示应用程序中的一些标记,必要时您可以根据应用程序逻辑回滚到事务保存点。
要使用这种功能,必须将 Connection 对象的 auto-commit 模式设置为 OFF,这可以通过使用 setAutoCommit 方法,以 false 为参数来完成。定义保存点和回滚在代码中都不难实现。在清单 6 中,我们将 Connection 对象自治的 AutoCommit 值设置为 false,然后执行两次更新。接着,我们定义一个名为 SavePoint1 的保存点,再执行两次更新。当执行接下来的一行 — db2Connection.rollBack(savePoint) — 时,便回滚到前两次更新后的状态。
| // set auto-commit mode to off db2Connection.setAutoCommit(false); Statement statement = db2Connection.createStatement(); statement.executeUpdate(firstUpdateStringStatement); statement.executeUdpate(secondUpdateStringStatemet); // define a Savepoint marker Savepoint savePoint = db2Connection.setSavePoint("SavePoint1"); statement.executeUpdate(thirdUpdateStringStatement); statement.executeUpdate(fourthUpdateStringStatement(); // roll back to the savepoint db2Connection.rollBack(savePoint); // commit the changes db2Connection.commit(); |
当您想不遵从更改时,这种方式的回滚比较有用。回滚可以结合 Boolean 逻辑,这样一来便可以设计复杂的业务事务。
游标保持能力。在 JDBC 3.0 之前,当提交事务时,在事务期间创建的任何 ResultSet 对象的游标都将被自动关闭。而对于 JDBC 3.0,您可以撤消这种效果,方法是将 ResultSet.HOLD_CURSORS_OVER_COMMIT 常量提供给 Connection 对象的 createStatement 或 prepareStatement 方法。因此,您可以对结果集中的一组记录执行一组更新操作,然后将更改提交给 DB2 UDB 服务器。这样可以将记录上的 DB2 服务器锁释放出来,而使其他应用程序得以看见更改过的数据,并允许应用程序继续处理 ResultSet。
更多内容
DB2 Universal JDBC Driver 是 DB2 家族中的战略组成部分,我们只是略微谈到 DB2 UDB v.8.2 中的一些 JDBC 增强。在 DB2 UDB 中还可以发现很多的代码示例,包括完整的 JDBC 驱动程序文档细节(在“Application Development Guide: Programming Client Applications”中)。新的基于 Eclipse 技术的 DB2 UDB v.8.2 Information Center 为对 DB2 开发人员和 DB2 管理员有用的信息提供了极好的搜索功能。

