程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> DB2 應用程序開發: 利用 DB2 Universal JDBC Driver 進行跟蹤

DB2 應用程序開發: 利用 DB2 Universal JDBC Driver 進行跟蹤

編輯:DB2教程

應用程序和數據庫之間接口上的跟蹤數據為開發人員提供了發現程序錯誤和優化數據庫訪問所需的信息。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 對本文進行了審校。

本文示例源代碼或素材下載

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved