使用SQLJ進行數據庫開發 第二部分:SQLJ語言元素

在我的第一篇文章中,我描述了什麼是SQLJ,把它同PL/SQL和JDBC進行了比較,並分析了從SQLJ獲得的好處。在這篇文章中,我闡述了SQLJ編程語言的基本原理,這樣你能做好準備,以使用真正的SQLJ。
   
    SQLJ程序是嵌入了SQL語句的標準Java程序,這些SQL語句從#sql標記開始,到分號結束。有兩種SQLJ語句:聲明和可執行語句。
   
    聲明語句宣佈連接環境和迭代器。連接環境被用於建立數據庫連接,而迭代器被用於保存SQL查詢的結果集。這二者中,後者被用得更多。可執行語句執行嵌入的SQL語句和PL/SQL塊。因爲SQLJ程序要被翻譯和通過JDBC運行,所以任何被JDBC驅動程序支持的SQLJ語句(參加下面內容)都可以被嵌入進SQLJ可執行語句。可執行語句也可以包含主機表達式,通過Java變量,它被用來在Java程序和數據庫之間交換信息。
   
    Oracle JDBC驅動程序
    Oracle提供下面的JDBC驅動程序:
    客戶端廋驅動程序是一個100%的Java驅動程序,由沒有安裝Oracle的客戶端使用,Oracle推薦通過applets使用它。當Java applet運行時,瀏覽器能夠下載這些驅動程序。
    OCI驅動程序(OCI8和OCI7)通過Oracle客戶安裝,客戶端使用這些驅動程序。Oracle JDBC OCI驅動程序通過從Java直接調用Oracle調用接口(OCI)訪問數據庫,提供Oracle 7、8和8i版本之間最高的兼容性。這些驅動程序需要通過Net8的Oracle客戶安裝。
    服務器端廋驅動程序提供同客戶端廋驅動程序相同的功能,但是它運行在Oracle數據庫內部,並且可以訪問遠程數據庫。作爲一箇中間件,從一個Oracle服務器訪問一個遠程的Oracle服務器,或者更一般地,從內部其它的Oracle服務器,如從任何Java存儲過程或EJB訪問一個Oracle服務器時,這種驅動程序很有用。
    服務器端內部驅動程序,叫做KPRB(核心程序包),支持運行在完成SQL操作的目標Oracle數據庫內部的任何Java代碼。服務器端內部驅動程序允許JServer JVM直接同SQL引擎通訊,在Oracle 8i/9i服務器上,它是作爲存儲過程、存儲函數、中間件、EJB或者CORBA對象運行的Java代碼的默認JDBC驅動程序。
   
    由於運行在Oracle JServer內部,同時被專門優化,KPRB JDBC驅動程序非常靈活和有效率。你在寫SQLJ存儲過程時,將會用到它。
   
    接下來,讓我們分析描述的SQLJ元素:連接環境、迭代器、可執行語句和主機表達式。 
   
    連接環境
    要建立一個簡單的連接,在構造DefaultContext對象的時候,可以使用一個DefaultContext的實例,指定數據庫的URL、用戶名和密碼來實現,而最容易的方法是使用由Oracle公司提供的oracle.sqlj運行時的connect()方法。在下面的例子中,我們將使用JDBC廋驅動程序,通過1521端口把用戶“scott”連接到服務器MYSERVER的數據庫,密碼是“tiger”,ORCL是被連接的數據庫的SID:
    Oracle.connect("jdbc:oracle:thin@MYSERVER:1521:ORCL", "scott", "tiger");
    這個例子創建DefaultContext類的一個實例,並且把它安裝爲你默認的連接。有了這個DefaultContext的實例,不再需要其他的任何東西。
   
    要建立多個連接,可以通過Oracle.getConnection()方法,創建和使用DefaultContext類另外的實例。在在下面的例子中,我們將使用Oracle OCI8驅動程序,把MYSERVER_ORCL作爲一個Oracle服務器的名字,在TNSNames.ora中創建一個ORCL實例:
    DefaultContext myContext1 = Oracle.getConnection
    ("jdbc:oracle:oci8@MYSERVER_ORCL", "scott", "tiger");
    DefaultContext myContext2 = Oracle.getConnection
    ("jdbc:oracle:oci8@MYSERVER_ORCL ", "tom", "bear");

   
    這段代碼創建兩個連接環境實例,他們都使用相同的Oracle OCI8驅動程序,但是使用不同的schemas。通過爲每一個語句指定連接,在這兩個schemas中都可以完成SQL操作:
    #sql [myContext1] { SQL statement };
    ...
    #sql [myContext2] { SQL statement };

   
    在程序的最後,我們必須提交或者撤銷所有的修改,並且在FINALLY子句和TRY/CATCH塊中關閉連接:
    finally
    {
       #sql [myContext1] { commit };
       myContext1.close();
   
       #sql [myContext2] { commit };
       myContext2.close();
    }
    ...

   
    迭代器(iterator)
    iterator對象用於保存數據,在SQLJ程序中,SQL查詢返回的結果集也可以用iterator對象表示。iterator對象是iterator類的實例,概念類似於PL/SQL的遊標。
   
    要使用iterator處理SQL查詢返回的行(rows,可以理解爲記錄),必須完成以下五步:
    1.聲明iterator類。
    2.聲明一個來自iterator類的iterator對象。
    3.使用一個SELECT語句填充iterator對象。
    4.從iterator對象讀出各行。
    5.關閉iterator對象。
   
    有兩種iterator類:
    1.命名(Named)iterators,必須指定Java變量類型和iterator列名(column name)。
    2.定位(Positional)iterators,只有被檢索的數據庫列的Java變量必須被指定。 
   
    命名Iterators:一個命名iterator聲明同時指定列的訪問名稱和他們的Java變量。 
   
    下面讓我們用例子示範所有五個步驟,假設想檢索Emp表的Ename、Job和HireDate列,用於查看薪水超過1500的職員。
   
    1.聲明iterator類:
    #sql iterator EmpIteratorClass(String Ename, String Job, Timestamp HireDate);
    Java String類被用於表示Ename和Job列,因爲它與Oracle VARCHAR2數據庫的數據類型兼容。java.sql.Timestamp類型被用於表示HireDate列(Oracle的DATE數據類型),因爲java.sql.Date類型只能保存年、月、日和時間信息,不能像java.sql.Timestamp一樣可以保存時、分和秒。
   
    2.聲明一個來自iterator類的iterator對象:
    EmpIteratorClass empIterator;
   
    3.使用一個SELECT語句填充iterator對象。下面的SQLJ語句用Emp表的Ename、Job和HireDate列填充empIterator對象:
    int salary = 1500;
    #sql empIterator = {
       select Ename, Job, HireDate
          from Emp
          where Sal > :salary
    };

    我們也聲明主機變量salary,它被用於WHERE子句,識別從Emp表檢索的行。記住,SQL查詢返回的數據庫列的名字必須與在第一步定義的iterator列名相符。
   
    4.從iterator對象讀出各行。因爲iterator對象可以包含多個行,我們必須使用循環依次訪問每一行――我們從PL/SQL遊標讀出每一行時,使用了相同的方法。命名iterator 實現了一個next()方法,調用它可以從頭到尾移動iterator對象的行。另外,SQLJ提供了檢索iterator值的方法。 
    下面的代碼循環打印姓名、工作和僱傭的日期:
    while (empIterator.next()) {
       System.out.println("Name: " + empIterator.Ename());
       System.out.println("Job: " + empIterator.Job());
       System.out.println("Hire Date:" + empIterator.HireDate().toString());
    }

   
    5.關閉iterator對象:
    empIterator.close();
   
    清單1結合了第二到第五步,通過命名iterator和empSalary參數展示listEmployees()方法。
    清單1. Iterator類聲明在listEmployees()方法的外部
    public static void listEmployees(String empSalary)
    throws SQLException {
       EmpIteratorClass empIterator;
       Integer salary = new Integer(empSalary);
       try {
          #sql empIterator = {
             select Ename, Job, HireDate
                from Emp
                where Sal > :salary
          };
          while (empIterator.next()) {
             System.out.println("Name: "  + empIterator.Ename());
             System.out.println("Job: "  + empIterator.Job());
             System.out.println("Hire Date:" + empIterator.HireDate().toString());
          }
          empIterator.close();
       } catch (SQLException e) {
         System.err.println("SQLException" + e);
         System.exit(1);
       }
    }

   
    定位Iterators:同命名iterators相比,定位iterators僅僅指定列的編號(number)和類型,而不指出他們的名字。列數據可以僅僅通過他們的位置來訪問,同傳統的(指PL/SQL)FETCH…INTO語法完全相同。另外,使用FETCH語句,你也必須使用定位iterator的方法endFetch()來探測終止的條件以跳出循環。在取得的數據被訪問前,總是必須檢查這個條件。
   
    下面是使用定位iterator的例子時相同的五個步驟:
   
    1.聲明iterator類:
    #sql iterator EmpIteratorClass(String, String, Timestamp);
   
    2.聲明一個來自iterator類的iterator對象,同時聲明所有必須的主機變量,用於從iterator對象取得數據:
    EmpIteratorClass empIterator;
    String name = null;
    String job = null;
    Timestamp hireDate = null;

   
    3.使用一個SELECT語句填充iterator對象:
    int salary = 1500;
    #sql empIterator = {
       select Ename, Job, HireDate
          from Emp
          where Sal > :salary
    };

   
    4.從iterator對象讀出各行,保存到主機變量:
    while (true) {
       #sql { FETCH :empIterator INTO :name, :job, :hireDate };
       if (empIterator.endFetch()) {
          break;
       }
   
       System.out.println("Name: " + name);
       System.out.println("Job: " + job);
       System.out.println("Hire Date:" + hireDate().toString());
    }

   
    5.關閉iterator對象:
    empIterator.close();
   
    清單2結合了第二到第五步,通過定位iterator和empSalary參數展示listEmployees()方法。
    清單2. 使用定位Iterator和empSalary參數的listEmployees()方法:
    public static void listEmployees(String empSalary)
    throws SQLException {
       EmpIteratorClass empIterator;
       Integer salary = new Integer(empSalary);
       /* Host variables */
       String name = null;
       String job = null;
       Timestamp hireDate = null; 
       try {
          #sql empIterator = {
             select Ename, Job, HireDate
                from Emp
                where Sal > :salary
          };
          while (true) {
             #sql { FETCH :empIterator INTO :name, :job, :hireDate };
             if (empIterator.endFetch()) {
                break;
             }
   
             System.out.println("Name: "     + name);
             System.out.println("Job: "      + job);
             System.out.println("Hire Date:" + hireDate().toString());
          }
          empIterator.close();
       } catch (SQLException e) {
         System.err.println("SQLException" + e);
         System.exit(1);
       }
    }
   
    如你所見,我們使用與PL/SQL遊標語法非常相似的語法操縱了全部的定位iterator對象。命名iterators和定位iterators都完成相同的基本功能:保存SQL查詢返回的多行結果集。使用哪種類型的iterator是一件麻煩事,或者說出於個人的偏愛。從性能的觀點看,他們產生同樣的結果。
   
    可執行語句
    可執行的SQLJ語句包含一個位於大括號之內的靜態SQL操作。有兩種可能的可執行語句類型,這取決於SQL是否返回值。 
   
    這是一個沒有返回值的嵌入SQL語句的例子,它在Emp表中創建一個關於Ename和Sal列的複合索引:
    #sql { create index EMP_ENAME_SAL on Emp(Ename, Sal) };
    如果嵌入SQL語句返回值,必須使用主機變量,以指定結果放在哪裏。在這個例子中,調用PL/SQL 的getSalary函數返回Empno=7900的僱員的薪水。你可以使用VALUES或SET操作符調用函數,即:
    int salary;
    int empNo = 7900;
    #sql salary = { VALUES getSalary(:empNo) };
    或者;
    #sql { SET :salary = getSalary(:empNo) };

   
    主機表達式
    主機變量,如你在前面的例子中所見,允許SQLJ程序在數據庫和Java程序之間交換數據。他們是在Java程序中聲明並且在SQLJ語句中引用的任何Java變量。主機變量使用冒號前綴嵌入進SQLJ語句,並被主機表達式調用。他們綁定主機變量到SQLJ可執行語句,並且也可以包含Java數組元素、對象屬性或者Java函數,SQLJ將處理在SQL和Java環境之間來回地移動的數據。
   
    在SQLJ中,所有標準的JDBC類型――如Boolean、byte、short、int、String、byte〔〕、double、float、java.sql.Data等等――都是有效的主機表達式類型。另外,Oracle的SQLJ翻譯器支持使用Oracle類型,如ROWID、CLOB、BLOB,以及Object和REF類型。
   
    在這篇文章中,我講述了所有主要的SQLJ類型的對象,要開始寫實際的SQLJ代碼,這些都是必不可少的:連接環境、命名和定位迭代器(iterators)、可執行語句和主機表達式。
   
    Boris Milrud 有十年的軟件開發經驗,他是位於San Jose CA(聖何塞市)Callidus軟件公司的高級數據庫工程師,他專長於各種Oracle數據庫軟件開發,包括數據庫設計、編程、優化和調試。可以通過[email protected]和他聯繫。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章