程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> 在使用 SQLJ 和 JDBC 時獲取最優的 DB2 性能

在使用 SQLJ 和 JDBC 時獲取最優的 DB2 性能

編輯:DB2教程

簡介

我該學習本教程嗎?

本教程提供了技巧和技術,以幫助您最優化用 Java 應用程序訪問 DB2 V8.1,並且最優化對新的 IBM DB2Universal Driver for SQLJ and JDBC 1.0 的使用。本文還描述了如何避免可能影響這些應用程序性能的常見錯誤。

您應該熟悉 Java 編程和 DB2 的基礎知識以及相關概念。熟悉 SQL 和 JDBC 會有幫助,但並非必需。

本教程是關於什麼的?

Java 語言提供了兩種用來訪問關系數據庫中數據的接口:低級應用程序編程接口 JDBC 和允許您在 Java 程序中直接嵌入 SQL 語句的 SQLJ。

JDBC 和 SQLJ 都可以用來訪問各種關系數據庫(包括 DB2 和 Oracle)中的數據。本教程主要討論在使用 SQLJ 訪問 DB2 時如何獲得最佳性能。

所涵蓋的主題包括:

SQLJ 和 JDBC

SQLJ 基礎知識

靜態 SQL 和 SQLJ

通過 Java 應用程序優化 DB2 性能

避免 SQLJ 和 JDBC 中的常見錯誤

安全性、SQLJ 和 JDBC

同一程序中的 SQLJ 和 JDBC

工具

代碼樣本用來說明本教程中所討論的技巧和技術。要運行本教程中的一些樣本,請在開始之前安裝和測試下列工具:

符合 JDBC 規范的數據庫,如 DB2 通用數據庫 V8.1。可以免費下載 IBM DB2 通用數據庫 V8.1 試用版。DB2 包含對 SQLJ 的支持。

Java 2 SDK,標准版,V1.3.1 或更新版本。

概述

Java 語言和數據訪問的發展

在過去的五年中,Java 語言得到了越來越廣泛的使用。自從 1992 年誕生以來,它迅速地成為了世界上應用最為廣泛的編程語言之一。據估計,目前 Java 程序員的人數超過了 250 萬。

Java 語言有兩個主要特征:

它是一種面向對象的語言。Java 語言使用類,類是由實現類功能的方法和保存類數據的字段組成。Java 類充當對象的模板,對象則是作為類的實例而創建的。類可以是另一個類的子類,從父類繼承字段和方法。Java 語言還有接口,它們是對特定類中所用方法的描述。通常用類來實現接口中的方法。最後,您可以通過在程序中包含庫來將接口和類添加到 Java 程序。

它是跨平台的。Java 語言可以在當前流行的大多數操作系統上運行。盡管不同平台上的 Java 實現可能有細微的差別,但 Java 語言的核心在任何平台上都是相同的。這種共性意味著您可以在任何受支持的平台上進行開發,而不必再經歷陡峭的學習曲線,或為了在某種平台上運行而修改代碼。

Java 語言包括許多不同的接口和類,用來向 Java 程序提供功能。

功能的主要方面之一就是訪問要在 Java 程序中使用的數據。在 Java 語言中,有兩種用於數據訪問的開放標准方法:JDBC 和 SQLJ。

JDBC

JDBC 提供了通過調用級別接口從關系數據庫中訪問數據的基本功能。使用 JDBC 接口,您可以編寫用動態 SQL 語句從 DB2 中檢索數據的 Java 程序。使用 JDBC 時,您不必預先定義將要用來訪問數據的 SQL。

當用 JDBC 從 DB2 數據庫中的表中選擇數據時,下列代碼是必需的:

Java.sql.PreparedStatement prepStmt =
    context.prepareStatement("SELECT EMPNO FROM EMP_ACT WHERE PROJNO =?");
prepStmt.setString(1, strName);
Java.sql.ResultSet rs = prepStmt.executeQuery;
   
//RetrIEve data
   
rs.close();

SQLJ

SQLJ 構建在 JDBC 之上,使用嵌入式 SQL 來訪問數據庫。使用 SQLJ 只需要將 SQL 語句嵌入到 Java 代碼中 ― 而不必處理底層的調用級別接口。

要用 SQLJ 執行(上一頁所描述的)SELECT 語句,請使用以下代碼:

#sql [context] {SELECT EMPNO INTO :strEmpNo FROM EMP_ACT WHERE PROJNO =:strProjNo};

除了減少一些編碼需求之外,使用 JDBC 和使用 SQLJ 訪問 DB2 的主要差異就在於 JDBC 總是使用動態 SQL。SQLJ 可以使用動態 SQL,但也可以通過定制過程,使之能夠使用靜態 SQL 語句,靜態 SQL 一章將討論這一主題。

SQLJ 和 JDBC 之間的另一個重要差異是對預編譯步驟的使用。必須通過預編譯器運行帶 SQLJ 語句的 Java 程序,以將 SQLJ 偽指令轉換成 Java 代碼。

SQLJ 和 JDBC

本教程著重討論使用 SQLJ 來最優化 DB2 性能。我們為什麼使用 SQLJ 而不用 JDBC 呢?

有以下幾個使用 SQLJ 的理由:

完成同樣的數據訪問任務,SQLJ 所需要的代碼行往往更少。更少的代碼行數意味著在開發應用程序上花費的時間更少,調試和維護應用程序所花費的時間也更少。

使用 SQLJ,可以使用定制過程來檢查程序內 SQL 語句的語法。這個過程消除了發生運行時錯誤的可能性。

SQLJ 用游標和語句實現 SQL,這和其它編程語言中的 SQL 很相似。如果您對用其它語言創建應用程序很熟悉,但在 Java 編程方面是個新手,那麼使用 SQLJ 有助於降低您的整體學習曲線。

JDBC 可以做一件 SQLJ 不能做的事情 ― 執行動態 SQL 語句。對於需要使用一些動態 SQL 的應用程序,您總是可以在程序中包含 JDBC 代碼的同時包含一些 SQLJ 代碼。有關在應用程序中包含這兩者的信息,以及一些使用 JDBC 的技巧,請參閱同一應用程序中的 SQLJ 和 JDBC。

下一章討論了使用 SQLJ 的基本語法。

SQLJ 基礎知識

准備

為了在 Java 程序中使用 SQLJ 來訪問 DB2,您需要在開始編碼之前采取一些步驟。在應用程序的目錄或 CLASSPATH 中包括下列文件。

db2jcc.jar,它提供 JDBC 驅動程序(類型 4)

sqlj.zip ,它提供 SQLJ 轉換程序類文件,在 SQLJ 中將有所討論

這些文件都位於 SQLLIB/Java 目錄。

在代碼中,可以用下列 import 語句來包括所需的類和接口:

import Java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;

基本 SQLJ 語法

SQLJ 將 SQL 用作訪問和操作數據庫中數據的方法。為了使用這些嵌入在 Java 程序中的 SQL 語句,請使用 SQLJ 預編譯器可以識別的語法。

任何嵌入式 SQLJ 語句都必須遵守兩個簡單規則:

語句必須由語法 #sql 開頭。

語句必須由分號(;)結尾。

您還應該將 SQLJ 語句放在大括號中,並且還要寫上執行該語句的上下文(盡管這是可選的)。下列代碼段說明了這些需求和建議的使用:

#sql [context] {DELETE FROM EMP_ACT};

注:本教程中使用的所有表都來自於 DB2 SAMPLE 數據庫。

從應用程序傳遞信息

前一頁中使用的簡單 SQL 語句不需要從發出該語句的 Java 應用程序向該語句傳遞任何信息。但某些類型的 SQL 語句(如 INSERT)需要從 Java 程序向嵌入式 SQL 語句傳遞數據。

應用程序可以通過使用主機變量來傳遞這種數據。主機變量只是一個變量,它是執行調用的 Java 程序的一部分,由一個冒號(:)開頭,以表明其來源。

例如,要對 EMP_ACT 表進行插入操作,請使用下列語法:

void m (String empno, String projno, int actno) throwsSQLException
{
 #sql [context]{INSERT INTO EMP_ACT (EMPNO, PROJNO, ACTNO)
                  values (:empno, :projno,:actno)};
}

完整的應用程序

既然您已經理解了 SQLJ 的基本知識,那麼可以將它全部用於完整的應用程序。正如本教程先前部分所描述的,這個應用程序的步驟包括創建 URL 和連接以訪問的數據庫,以及使用 SQLJ。

此外,這個示例還包括異常處理和用於注冊 DB2 JDBC 驅動程序的代碼。

import Java.sql.Connection;
import Java.sql.DriverManager;
import sqlj.runtime.ref.DefaultContext;
   
public class SqlJDemo
{
 static
 {
  try
  {
   //register the DB2 JDBC driver with DriverManager
   Class.forName("com.ibm.db2.jcc.DB2Driver").newInstance();
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
   
 static Connection con;
 static DefaultContext ctx;
   
 #sql iterator NamedIterator (String empno, String projno, int actno );
 #sql iterator PosIterator (String, String, int);
   
 public static void main (String args[]){
   SqlJDemo demoApp = new SqlJDemo();
   demoApp.makeConnection();
   demoApp.insertData();
   try {
    //Commit any remaining transactions
    #sql [ctx] {commit};
    ctx.close();
    con.close();
   } catch(Java.sql.SQLException sqle) {
    sqle.printStackTrace();
   }
 }
   
 public void makeConnection()
 {
  try
  {
   // get context and open connection to DB2
   ctx = DefaultContext.getDefaultContext();
   if (ctx == null)
   {
    //construct the URL (sample is the database name)
    String url = "jdbc:db2://localhost:50000/SAMPLE";
   
    //connect to the 'sample' database with user ID and passWord
    con = DriverManager.getConnection(url, "myusername", "mypassWord");
    con.setAutoCommit(false); 
   
    ctx = new DefaultContext(con);
    DefaultContext.setDefaultContext(ctx);
   }
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
 }
   
 public void insertData()
 {
  try
  {
   String empno = null;
   String projno = null;
   int actno;
   
   // Set values for host variables
   empno = "01";
   projno = "10";
   actno = 1;
   
   // insert data into EMP_ACT
   #sql [ctx] {INSERT INTO EMP_ACT (EMPNO, PROJNO, ACTNO)
                 values (:empno, :projno, :actno)};
   // commit inserted record
   #sql [ctx] {commit};
  } catch(Exception e) {
   e.printStackTrace();
  }
 }
   
}

檢索多行數據

到目前為止,所有示例使用的 SQL 語句都不返回數據。要從 DB2 返回一行或多行數據,使用 SELECT 語句。SELECT 語句返回多個行,它們是 SQL 查詢的結果。檢索和使用這個結果集中的行需要一些額外的 SQLJ 語法。

因為結果集可以包括多行數據,所以需要聲明一個迭代器來遍歷這些行。而且必須在任何方法以外聲明迭代器,因為當對它們執行 SQLJ 轉換程序時,會將它們轉換成單獨的類。然後,您可以創建一個迭代器實例並填充該實例。

您可以使用兩種不同類型的迭代器 ― 指定名稱迭代器或位置型迭代器。對於指定名稱迭代器,用列名及其數據類型聲明迭代器,其語法如下所示:

#sql iterator NamedIterator (String empno, String projno, int actno);

要檢索數據,請創建一個迭代器類實例並通過賦給它 SQL 語句來填充它。然後,您可以通過使用 while 循環來循環遍歷該數據,用 NamedIterator 類中創建的方法檢索每個列:

...
  }
 }
 static Connection con;
 static DefaultContext ctx;
 #sql iterator NamedIterator (String empno, String projno, int actno );
 public static void main (String args[]){
   SqlJDemo demoApp = new SqlJDemo();
   demoApp.makeConnection();
   demoApp.insertData();
   demoApp.useNamedIterator();
 }
 public void useNamedIterator()
 {
  try
  {
    NamedIterator namedIter = null;
    #sql [ctx] namedIter = {SELECT EMPNO, PROJNO, ACTNO FROM EMP_ACT};
    while (namedIter.next())
    {
      System.out.println(namedIter.empno()+"  "
           +namedIter.projno()+"  "+namedIter.actno());
    }
    namedIter.close();
  } catch (Exception e) {
    e.printStackTrace();
  }
 }
 public void makeConnection()
 {
  try
  {
...

位置型迭代器

對於位置型迭代器,聲明迭代器時只需指定數據類型,其語法如下所示:

#sql iterator PosIterator (String, String, int);

您還需要聲明主機變量來檢索數據,並在程序代碼中創建迭代器的實例。使用 FETCH ... INTO 語法來將列檢索到主機變量中,其語法如下所示:

...
 static Connection con;
 static DefaultContext ctx;
 #sql iterator NamedIterator (String empno, String projno, int actno);
 #sql iterator PosIterator (String, String, int);
 public static void main (String args[]){
   SqlJDemo demoApp = new SqlJDemo();
   demoApp.makeConnection();
   demoApp.insertData();
   demoApp.useNamedIterator();
   demoApp.usePositionalIterator();
 }
 public void usePositionalIterator()
 {                         
  try
  {
    String empno = null;;
    String projno = null;
    int actno = -1;
    PosIterator posIter = null;
    #sql [ctx] posIter = {SELECT EMPNO, PROJNO, ACTNO FROM EMP_ACT};
    #sql {FETCH :posIter INTO :empno, :projno, :actno};
    while (!posIter.endFetch())
    {
      // row logic
      System.out.println(empno+"  "+projno+"  "+actno);
      #sql {FETCH :posIter INTO :empno, :projno, :actno};
    }
    posIter.close();
  } catch (Exception e) {
    e.printStackTrace();
  }
 }
 public void useNamedIterator()
 {
  try
...

編譯 SQLJ

在本教程前面的部分中,頁面 SQLJ 提到過必須通過預編譯器來運行包含 SQLJ 偽指令的 Java 代碼。這個預編譯器將 SQLJ 偽指令轉換成 Java 代碼。

要完成這個轉換,使用 sqlj 轉換程序。例如,這個示例中的應用程序名為 SqlJDemo.sqlj,因此轉換它涉及下列命令:

sqlj SqlJDemo.sqlj

這個示例展示了使用 sqlj 轉換程序的最簡單形式,它對如 DB2 驅動器路徑這樣的參數都采用缺省值。

sqlj 轉換程序創建一個名為 SqlJDemo.java 的文件,該文件包含從 SQLJ 偽指令生成的 Java 代碼。

轉換程序還為應用程序中的每個連接上下文類創建概要文件。對於這個應用程序,該概要文件的名稱為 SqlJDemo_SJProfile0.ser。如果這個應用程序有多個上下文類,那麼它們的名稱將是 SqlJDemo_SJProfile1.ser、SqlJDemo_SJProfile2.ser、SqlJDemo_SJProfile3.ser 等。

准備該 Java 程序的下一步是為程序中的靜態 SQL 在 DB2 中創建包。本教程的下一章將討論靜態 SQL。

靜態 SQL

什麼是靜態 SQL?

要理解靜態 SQL,就必須首先理解 DB2 是如何使用靜態 SQL 語句的。

SQL 是一種標准訪問語言,但每種數據庫都必須將這種標准語法修改為可以有效使用的調用。這種修改有三個基本步驟:

對 SQL 語法進行檢查,以找出語法錯誤,並確保語句中的數據庫對象存在並具有正確的數據類型。

對 SQL 語法進行最優化。DB2 通過這個過程來確定讀或寫語句中所表示的數據的最佳訪問路徑。對於簡單數據庫表,這種最優化過程的作用也許很有限,但如果對於一個需要對多個表進行操作的復雜的 SQL 語句,而這些表又帶有很多索引,那麼最優化過程可能必須考慮許多選項來獲得最佳路徑。

執行 SQL 語句。

對於動態 SQL,盡管 DB2 可以為重復的動態 SQL 高速緩存前兩個步驟的結果,但還是必須對每個 SQL 語句重復上面的所有步驟。

對於靜態 SQL,預編譯 SQLJ 應用程序時會創建關於靜態 SQL 語句的信息。然後您可以創建包(存儲在 DB2 中),它是上述過程中前兩個步驟的結果。當與包匹配的程序運行時,只是將 SQL 語句聯編到這些結果,這樣可以帶來更低的開銷和更佳的性能。

從 Java 代碼創建包

要完成使用 Java Common Connectivity(JCC)驅動程序的 SQLJ 應用程序的准備工作,您必須對預編譯過程期間生成的概要文件進行定制。定制將創建隨後可以聯編到數據庫的包。

要為 SQLJ 應用程序創建包,對前一章(SQLJ)中描述的、作為預編譯過程一部分創建的每一個服務器概要文件使用 db2sqljcustomize 命令。如果不對 SQLJ 應用程序使用這條命令,則應用程序將動態執行程序中的 SQL 語句。

db2sqljcustomize 命令的語法如下:

db2sqljcustomize -user username -password passWord -url url
          profilename

其中,username 和 passWord 是在連接到數據庫時使用的用戶名和該用戶名的密碼,url 是數據庫的 URL。您還可以使用 onlinecheck 開關檢查大多數嵌入在應用程序中的 SQL 語句。

例如,為樣本應用程序定制的概要文件使用了下列命令:

db2sqljcustomize -user myusername -password mypassWord
-url jdbc:db2://localhost:50000/sample -onlinecheck YES SqlJDemo_SJProfile0.ser

profilename 是作為 SQLJ 編譯過程一部分而生成的概要文件的名稱。請記住,應用程序中所使用的每個連接上下文都有一個概要文件。

缺省情況下,定制自動將所生成的包聯編到數據庫,但也可以單獨地使用 db2sqljbind 來做到這一點,如下所示:

db2sqljbind -user myusername -password mypassWord
     -url jdbc:db2://localhost:50000/sample SqlJDemo_SJProfile0.ser

您可以用 db2sqljprint 打印出 DB2 概要文件的可讀純文本版本。這個命令使用如下語法:

db2sqljprint profilename

最優化 DB2 性能

最優化性能概述

前一章討論了靜態 SQL 的使用,靜態 SQL 在許多情況下能夠比 JDBC 所用的動態 SQL 運行得更快。本章將討論許多其它技術,您可以在使用 SQLJ 或 JDBC 時用它們來最優化性能。

禁用 AutoCommit

在本教程第一部分所示的示例中,您建立了到 DB2 數據庫的連接。如果您使用 JDBC 建立連接,那麼缺省情況下,對於數據庫,AutoCommit 特性是打開的。

AutoCommit,顧名思義就是對提交到數據庫的每個 SQL 語句,都進行自動提交。這種自動功能可以減少您必須進行的 SQL 編碼工作量,但它同時會使 DB2 的整體響應時間變慢,因為每個語句在執行時都需要額外的開銷。

AutoCommit 特性還可能會影響應用程序的整體完整性,因為您最終有可能無法回滾應用程序所進行一系列更改。

要關閉連接的 AutoCommit,使用下列代碼來創建連接:

con = DriverManager.getConnection(url, userid,passWord);
con.setAutoCommit(false);

限制列

SQL 是一種簡明而又功能強大的語言。只用幾個簡單的單詞,譬如:

SELECT * FROM EMP_ACT

就可以從表中檢索所有數據。

但僅因為可以這樣做並不意味著應該這樣做!因此雖然上述語句很容易編寫,但對於 DB2,它采用如下形式:

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