DB2 应用程序开发: 利用 DB2 Universal JDBC Driver 进行跟踪
2009-11-23 00:00:00 来源:WEB开发网应用程序和数据库之间接口上的跟踪数据为开发人员提供了发现程序错误和优化数据库访问所需的信息。DB2® Legacy JDBC™ Driver 基于 DB2 Call Level Interface (CLI) 层,它允许通过 CLI 配置的变化进行 JDBC 或 CLI 跟踪。而新的 DB2 Universal JDBC Driver 不再基于 DB2 CLI 层,所以那些已知的跟踪功能不再可用。但是 DB2 Universal JDBC Driver 通过对某些驱动程序属性的设置,同样提供了跟踪功能。本文首先提到 DB2 Legacy JDBC Driver 基于 CLI 的跟踪功能,然后描述新的 DB2 Universal JDBC Driver 的跟踪功能,并通过例子演示如何使用这些跟踪功能。
简介 —— 为什么需要 JDBC 跟踪?
JDBC 跟踪可以为 Java 应用程序开发人员提供有价值的信息,以帮助他们进行数据库应用程序的开发。在下面这些例子中,您需要知道如何执行 JDBC 跟踪:
搜寻程序逻辑错误或初始化错误 —— 错误的 URL 可能导致数据库连接失败,预期仅执行一次的查询可能被多次调用,定义有误的事务可能导致数据库的不一致,等等。在所有这些情况中,往往可以用跟踪数据来发现问题的起因。
性能调优 —— 在多层环境中,性能问题难于检测,因为首先必须确定应该对性能问题负责的层 - 应用程序、网络或数据库。通过分析跟踪数据流中函数调用的入口/出口时间戳,就可以发现是哪一层导致性能问题。
理解第三方的软件 —— 当您在使用第三方的软件时,由于手头上没有源代码,所以常常难于判别问题。因此,跟踪信息或许有助于更好地理解第三方软件是如何实现数据库接口的。
DB2 Legacy JDBC Driver 与 DB2 Universal JDBC Driver
DB2 Legacy JDBC Driver 是老的 type 2 驱动程序,老版本的 DB2 中自带了这种驱动程序。该驱动程序建立在 DB2 CLI 的基础上,DB2 CLI 是一种 DB2 本地 C 调用级接口,它本身建立在一些其他层之上。作为 type 2 驱动程序,它要求安装 DB2 客户机。DB2 Legacy JDBC Driver 包含在文件 db2java.zip 中。
DB2 version 8 附带了一种新的 JDBC 驱动程序,即 DB2 Universal JDBC Driver。该驱动程序是从头开始编写的,在架构中它是一个抽象 JDBC 处理器,用于支持 type 2 和 type 4 连接。在建立连接时,应用程序通过使用不同的 URL 语法来选择所需类型的连接。DB2 Universal Driver 使用 Distributed Relational Database Architecture (DRDA) 协议与 DB2 服务器通信,它建立在一个 Java 客户机层之上,这个层替代了 CLI 以及其下的很多层。只有 type 2 才要求安装 DB2 客户机,type 4 不需要 DB2 客户机。DB2 Universal JDBC Driver 包含在文件 db2jcc.jar (JCC 表示 Java Common Connectivity)中。虽然 DB2 Legacy Driver 仍然受支持,但在将来它将完全被 DB2 Universal JDBC Driver 替代。
用 DB2 Legacy JDBC Driver Type 2 进行跟踪
基于 CLI 的 DB2 Legacy JDBC Driver Type 2 为应用程序开发人员提供了两种不同的跟踪方法:
在 JDBC 层跟踪 —— 在这种情况下,所有 JDBC 函数调用都被跟踪。跟踪信息包括:函数调用序列、输入和输出参数、返回代码以及错误和警告消息。
在 CLI 层跟踪 —— DB2 Legacy JDBC Driver Type 2 在内部将所有 JDBC 函数调用映射到 CLI 函数调用上。因此,使用这种驱动程序的 Java 程序也可以在 CLI 层启动跟踪。跟踪信息仍然是一样的 —— 函数调用、参数、返回代码和消息 —— 但位置却是在更低级的 CLI 层。
上述两种跟踪都是通过 CLI 初始化文件 db2cli.ini 来控制的。在 Windows 系统上,文件 db2cli.ini 位于目录 %DB2PATH% (默认情况下是 C:\Program Files\IBM\SQLLIB)中。在 UNIX/Linux 系统上,该文件位于实例所有者的 $HOME 目录中,这个目录在 sqllib/cfg 目录下。
为了在 JDBC 层启动跟踪,在 db2cli.ini 文件中的 [COMMON] 下建立以下条目:
清单 1. 用于 JDBC 跟踪的 CLI 关键字[COMMON]
JDBCTrace=1
JDBCTracePathName=<trace directory>
可选地,也可以指定关键字 JDBCTraceFlush=1。在此情况下,每个跟踪条目被分开来写到跟踪文件中,之后关闭跟踪文件,再重新打开跟踪文件。这样可以保证,即使 Java 程序崩溃也不会丢失跟踪条目。但是另一方面,这个选项也极大地降低了性能。
当第一次建立数据库连接时,文件 db2cli.ini 中的条目是只读的,因此,对文件 db2cli.ini 的更改不会影响现有的数据库连接。
下面的示例程序可用于测试使用 DB2 Legacy JDBC Driver Type 2 情况下的 JDBC 跟踪。在本文的后续部分,该示例程序还用于演示 DB2 Universal JDBC Driver 的跟踪功能。
清单 2. 使用 Legacy Driver Type 2 情况下用于跟踪测试的示例代码public class LegacyTraceExample
{
public static void main(String[] args) {
try {
// load driver
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
// set connection properties
String databaseUrl = "jdbc:db2:sample";
// get connection
java.sql.Connection con =
java.sql.DriverManager.getConnection(databaseUrl, "user", "password");
// execute a query
java.sql.Statement stmt = con.createStatement();
String query = "SELECT COUNT(*) FROM SYSCAT.TABLES";
java.sql.ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
System.out.println("\n" + query + " = " + rs.getInt(1));
}
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
因为该示例程序只用于演示跟踪功能,因此这里最小限度地使用了异常处理。程序首先动态装载 DB2 Legacy JDBC Driver Type 2。然后通过 JDBC 驱动程序管理器建立到数据库的一个连接,并执行对系统目录表的一个简单查询。
db2cli.ini 文件中的下列条目在目录 C:\temp 中为示例程序创建一个 JDBC 跟踪。
清单 3. 使用 Legacy Driver Type 2 情况下用于跟踪测试的 db2.cli 条目[COMMON]
JDBCTrace=1
JDBCTraceFlush=1
JDBCTracePathName=c:\temp
用 DB2 Universal JDBC Driver Type 2/Type 4 进行跟踪
DB2 Universal JDBC Driver 不再基于 DB2 CLI 层。对于 type 4 驱动程序的初始化和 type 2 驱动程序的初始化都是如此。由于这个原因,通过 CLI 配置(文件 db2cli.ini)的变化来进行 JDBC 跟踪已经变得不可能。但是,DB2 Universal JDBC Driver 允许通过设置某些驱动程序属性进行跟踪。
当使用 DataSource 接口进行数据库访问时,可以通过该接口的方法来设置跟踪属性。DB2 Universal JDBC Driver 的所有 DataSource 类都继承基类 DB2BaseDataSource,这个基类也定义了用于跟踪的一些属性。
清单 4. DB2 DataSource 类com.ibm.db2.jcc.DB2BaseDataSource
com.ibm.db2.jcc.DB2SimpleDataSource
com.ibm.db2.jcc.DB2DataSource
com.ibm.db2.jcc.DB2ConnectionPoolDataSource
com.ibm.db2.jcc.DB2XADataSource
下面的例子中使用 DB2SimpleDataSource 进行数据库访问。JDBC 跟踪的配置对于所有 DataSource 类来说其行为都是一样的,并且也独立于 DB2 Universal JDBC Driver 的 type 2 或 type 4 初始化。
下面的示例程序演示了如何通过调用类 DB2BaseDataSource 中定义的方法来启动 JDBC 跟踪。
清单 5. 使用 Universal Driver Type 2/4 (变种 1)情况下的跟踪测试示例代码public class JccTraceExample1
{
public static void main(String[] args) {
try {
// create data source
com.ibm.db2.jcc.DB2SimpleDataSource ds =
new com.ibm.db2.jcc.DB2SimpleDataSource();
// set connection properties
ds.setServerName("localhost");
ds.setPortNumber(50000);
ds.setDatabaseName("sample");
ds.setDriverType(4);
// set trace properties
ds.setTraceDirectory("c:\\temp");
ds.setTraceFile("trace");
ds.setTraceFileAppend(false);
ds.setTraceLevel(com.ibm.db2.jcc.DB2BaseDataSource.TRACE_ALL);
// get connection
java.sql.Connection con = ds.getConnection("user", "password");
// execute a query
java.sql.Statement stmt = con.createStatement();
String query = "SELECT COUNT(*) FROM SYSCAT.TABLES";
java.sql.ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
System.out.println("\n" + query + " = " + rs.getInt(1));
}
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先初始化用于数据库访问的 DB2SimpleDataSource,在这个例子中,它被初始化为 type 4。JDBC 跟踪的配置是通过调用以下方法完成的:
setTraceDirectory —— 定义用于写跟踪文件的目录。如果定义一个跟踪目录,则对于每个数据库连接,都创建一个单独的跟踪文件。之所以建议这样做,是因为如果不这样做的话,所有数据库连接的跟踪数据都将被写到同一个跟踪文件 —— 这使得对跟踪文件的分析更加困难。
setTraceFile —— 定义跟踪输出所写到的文件。如果指定一个跟踪文件名,以及一个跟踪目录 —— 例如这个例子 —— 对于每个数据库连接,都按照下面的模式创建一个跟踪文件:<trace directory>\<trace file>_<data source type>_<sequential number>。对于示例程序,将创建一个跟踪文件 c:\temp\trace_sds_0。如果没有指定跟踪目录,那么所有数据库连接的跟踪输出都将被写到指定的跟踪文件。在这个例子中,跟踪文件也可以通过完全路径指定。
setTraceFileAppend —— 如果文件已经存在,控制是否覆盖跟踪文件。
setTraceLevel —— 定义要跟踪的信息。为此,要使用 DB2BaseDataSource 中定义的一些常量。
表 1. DB2 JDBC 跟踪常量
跟踪常量 | 整数值 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_NONE | 0 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_CONNECTION_CALLS | 1 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_STATEMENT_CALLS | 2 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_RESULT_SET_CALLS | 4 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_DRIVER_CONFIGURATION | 16 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_CONNECTS | 32 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_DRDA_FLOWS | 64 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_RESULT_SET_META_DATA | 128 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_PARAMETER_META_DATA | 256 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_DIAGNOSTICS | 512 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_SQLJ | 1024 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_XA_CALLS | 2048 |
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_ALL | -1 |
在示例程序中,所有跟踪信息都将被收集(com.ibm.db2.jcc.DB2BaseDataSource.TRACE_ALL)。如果您只想跟踪某些信息,那么可以通过使用 OR 操作符组合跟踪常量(例如 com.ibm.db2.jcc.DB2BaseDataSource.TRACE_STATEMENT_CALLS | com.ibm.db2.jcc.DB2BaseDataSource.TRACE_RESULT_SET_CALLS)。
这种跟踪可能有点问题,因为它要求改变源代码,这一点在所有环境中都是不可取的。如果独立于源代码来定义数据源,例如使用像 WebSphere 这样的应用程序服务器就可以做到这一点,那么,就可以和 Data Source 的定义一起指定跟踪属性。这样一来,就可以在不改变源代码的情况下启动/撤销跟踪。
但是,即使数据源是在源代码中定义的 —— 例如在这个示例程序中 —— 也可以在不改变源代码的情况下控制跟踪。清单 6 展示了不设置跟踪属性的示例程序。
清单 6. 使用 Universal Driver Type 2/4 情况下的跟踪测试示例代码(变种 2)public class JccTraceExample2
{
public static void main(String[] args) {
try {
// create data source
com.ibm.db2.jcc.DB2SimpleDataSource ds =
new com.ibm.db2.jcc.DB2SimpleDataSource();
// set connection properties
ds.setServerName("localhost");
ds.setPortNumber(50000);
ds.setDatabaseName("sample");
ds.setDriverType(4);
// get connection
java.sql.Connection con = ds.getConnection("user", "password");
// execute a query
java.sql.Statement stmt = con.createStatement();
String query = "SELECT COUNT(*) FROM SYSCAT.TABLES";
java.sql.ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
System.out.println("\n" + query + " = " + rs.getInt(1));
}
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
为了在不改变源代码的情况下控制跟踪,可以创建一个单独的配置文件,文件中包含一些跟踪属性。
清单 7. DB2 JDBC 跟踪属性db2.jcc.traceDirectory=c:\\temp
db2.jcc.traceFile=trace
db2.jcc.traceFileAppend=false
db2.jcc.traceLevel=-1
对于这个配置文件没有命名惯例。文件名是在执行 Java 程序时通过选项 -D 指定的。例如,如果配置文件被命名为 jcc.properties,那么程序调用如下:
清单 8. DB2 JDBC 跟踪属性文件java -Ddb2.jcc.propertiesFile=jcc.properties JccTraceExample2
在这个例子中,配置文件和 Java class 文件放在同一个目录中。如果不这样的话,也可以指定配置文件的完全路径。
如果使用配置文件,则不能将跟踪级别指定为常量,而应该使用相应的整数值,例如,对于 TRACE_ALL 是 -1,对于 TRACE_STATEMENT_CALLS | TRACE_RESULT_SET_CALLS 是 6(在这个例子中,这些值被简单相加“2 + 4”)。
由于配置文件中的属性不限于某一个数据源,因此它们自动引用所有数据源。所以,这里生成的跟踪文件名不同于在前一个例子中生成的跟踪文件名。如果同时指定了跟踪目录和跟踪文件,那么对于每个数据库连接,都按照下面的模式创建一个跟踪文件: <trace directory>\<trace file>_global_<sequential number>。对于当前的示例程序,创建的跟踪文件是 c:\temp\trace_global_0。
如果在源代码和配置文件中都指定跟踪属性,则使用源代码中定义的属性。如果要强制使用配置文件中的跟踪属性,则必须用加法覆盖指定配置文件中的跟踪属性。
清单 9. 跟踪属性覆盖的例子db2.jcc.override.traceDirectory=c:\\temp
db2.jcc.override.traceFile=trace
db2.jcc.override.traceFileAppend=false
db2.jcc.override.traceLevel=-1
使用 DB2 Universal JDBC Driver 时的跟踪变量
除了使用 Data Source 之外,也可以使用 DriverManager 接口来建立数据库连接。在这种情况下,可以在数据库 URL 中附加跟踪属性。
清单 10. 使用 Universal Driver Type 2/4 情况下的跟踪测试示例代码(变种 3)public class JccTraceExample3
{
public static void main(String[] args) {
try {
// load driver
Class.forName("com.ibm.db2.jcc.DB2Driver").newInstance();
// set connection properties
String databaseUrl = "jdbc:db2://localhost:50000/sample"
+ ":traceDirectory=c:\\temp"
+ ";traceFile=trace"
+ ";traceFileAppend=false"
+ ";traceLevel="
+ com.ibm.db2.jcc.DB2BaseDataSource.TRACE_ALL
+ ";";
// get connection
java.sql.Connection con =
java.sql.DriverManager.getConnection(databaseUrl, "user", "password");
// execute a query
java.sql.Statement stmt = con.createStatement();
String query = "SELECT COUNT(*) FROM SYSCAT.TABLES";
java.sql.ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
System.out.println("\n" + query + " = " + rs.getInt(1));
}
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果同时指定了跟踪目录和跟踪文件,则对于每个数据库连接,都按照下面的模式创建一个跟踪文件: <trace directory>\<trace file>_driver_<sequential number>。对于当前的示例程序,创建的跟踪文件是 c:\temp\trace_driver_0。
或者,也可以将跟踪输出重定向到 PrintWriter。在这种情况下,跟踪是通过调用类 com.ibm.db2.jcc.DB2Connection 的方法 setJccLogWriter 启动的。这个方法需要 PrintWriter 和跟踪级别作为参数。
清单 11. 使用 Universal Driver Type 2/4 情况下的跟踪测试示例代码(变种 4)public class JccTraceExample4
{
public static void main(String[] args) {
try {
// create print writer
java.io.PrintWriter printWriter = new java.io.PrintWriter(
new java.io.BufferedOutputStream(
new java.io.FileOutputStream("c:\\temp\\trace.txt"), 4096), true);
// create data source
com.ibm.db2.jcc.DB2SimpleDataSource ds =
new com.ibm.db2.jcc.DB2SimpleDataSource();
// set connection properties
ds.setServerName("localhost");
ds.setPortNumber(50000);
ds.setDatabaseName("sample");
ds.setDriverType(4);
// get connection
java.sql.Connection con = ds.getConnection("user", "password");
// activate trace
((com.ibm.db2.jcc.DB2Connection) con).setJccLogWriter(printWriter,
com.ibm.db2.jcc.DB2BaseDataSource.TRACE_ALL);
// execute a query
java.sql.Statement stmt = con.createStatement();
String query = "SELECT COUNT(*) FROM SYSCAT.TABLES";
java.sql.ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
System.out.println("\n" + query + " = " + rs.getInt(1));
}
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
致谢
我要感谢 Peter Schurr 对本文进行了审校。
本文示例源代码或素材下载
更多精彩
赞助商链接