近幾年來,Oracle開發人員都一直在使用PL/SQL|(一種提供了到關系數據庫語言SQL的過程擴展部分的語言)來構建管理大量的數據陣列的復雜系統。不幸的是,用PL/SQL寫的存儲過程只能在Oracle數據庫運行。但是SQL開發者有了一種寫代碼的強有力的工具,可以很容易的移植到其他的數據庫-這就是Java,因為它在跨平台開發和國際互聯網絡上的強大功能是它成為流行的開發語言。Java承諾的統一的、可移植的應用軟件開發解決辦法可以在簡單的、低成本的的IT基本設施上執行,所以主要的開發工具供應廠商和設備供應者都支持Java。居於領導地位的軟件供應廠商,例如Oracle和IBM,都在他們的數據庫和其他的應用程序平台上整合了Java虛擬機(JVM)。Oracle數據庫管理系統公司在Oracle 8i中引入了對Java的擴展支持功能。在Oracle中,有兩種使用Java的基本方法:
JDBC :就像ODBC一樣,它提供了一個基於驅動程序的接口,允許從Java應用程序中訪問Oracle數據庫。
SQLJ :這是多個廠商共同努力的結果,是一種新的語言,能提供在Java代碼中靜態SQL的支持。理論上,它提供了比JDBC更大程度的程序員勞動生產率。
在Oracle 8i數據庫服務器裡集成JVM是在最近幾年中Oracle引進的最重要的技術革新之一。集成JVM的Oracle,叫做JServer(亦稱Aurora JVM), 支持兩種不同的程序設計模型∶
和SQL集成,允許用戶使用Java編寫傳統的數據庫存儲過程、函數和觸發器。
用於分布式Java組件的事務服務器平台,稱作企業JavaBeans,允許程序員開發可重復使用的服務器端應用程序組件。
在本文中,我主要講述一下在Oracle 8i中SQL和Java集成。也就是說, Java可以調用SQL和PL/SQL,PL/SQL和SQL也可以調用Java。Java程序調用它們使用JDBC驅動程序的SQL和PL/SQL復本,而JDBC驅動程序是嵌入駐留在Oracle 8i數據庫中的JVM體系結構中的。另一方面,從SQL和PL/SQL到Java,Oracle 8i提供了二個特色。在Oracle 8i中,Java名稱空間映射到數據庫模式,易於從屬物允許Java被保存在數據庫中。Oracle 8i也提供擴展的Data Definition Language(數據定義語言DDL),例如CREATE PROCEDURE AS JAVA命令,因此Java代碼很容易內嵌入Oracle 8i中。
SQLJ是什麼?
SQLJ是一種允許把靜態的 SQL語句以文本形式嵌入Java程序中的語言。在寫一個SQLJ應用程序時,我們編寫一段Java程序然後遵循某些特定的標准法則把SQL語句嵌入在其中,這些法則定義了SQL語句怎樣寫入Java程序(具體情況請看Oracle 8i SQLJ開發指南與參考,你可以在Documentation Library光盤上找到)。
接下來,我們運行一個SQLJ翻譯器,通過把嵌入式結構化查詢語句替換為調用調用SQLJ運行時程序庫的方式把SQLJ程序轉換成一個標准的Java程序。生成的Java程序就可以使用任何標准的Java編譯程序(例如javac)來編譯了然後就可以配合數據庫使用了。SQLJ運行期環境是由一個瘦(即沒有額外系統開銷)SQLJ運行時程序庫組成,也就是說用純Java實現調用相應的數據庫(Oracle, DB2等等)的JDBC驅動程序。
SQLJ類似於其他的嵌入式結構化查詢語言的實現,像Oracle Pro * C (嵌入C語言環境的SQL)。SQLJ語言設計的目的就是幫助基於Java的程序員構建數據庫應用程序。SQLJ是一個ISO和ANSI標准,也就是說由領頭的數據庫與軟件供應廠商開發和支持的,包括Oracle數據庫管理系統公司,國際商業機器公司,美國賽貝斯公司, Informix公司,美國康柏公司等。所有這些公司合作開發兼容的SQLJ翻譯器來實現使用不同的數據庫。
SQLJ的優點
SQLJ編譯器提供了類型檢查和模式對象檢查來找出在SQL語句中的語法錯誤或遺漏或拼錯這樣的錯誤,這是在編譯過程中進行而不是在運行過程中進行。因此,使用SQLJ編寫的程序比使用JDBC編寫的程序更加健壯。
多廠商互用性 SQLJ語法是由主要的軟件供應廠商開發和支持的。因為SQLJ程序使用運行時JDBC調用訪問數據庫,所以SQLJ可以訪問任何JDBC驅動程序可以實現的數據庫服務器。
靈活的部署 因為SQLJ運行時程序庫是基於Java的程序,所以SQLJ應用程序可以在任何JDBC配置環境中配置,例如瘦客戶端,中間層或是數據庫服務器上等。
供應廠商具體定制 SQLJ通過後續的Java字節碼的定制支持供應廠商具體產品的特色和擴展。它可以被用來改善SQL查詢語言的執行性能,使用具體供應廠商提供的性能或功能上的擴展,而不用考慮SQLJ程序如何變化,以及調試和運行記錄等情況。
使用SQLJ的開發步驟
下面是開發和運行一個SQLJ應用程序所需要做的事情:
使用SQLJ編譯器編譯SQLJ源文件。這一步生成調用SQLJ運行時的Java文件,以及二進制的SQLJ描述文件--包括存在於SQLJ源文件中的靜態SQL語句的有關信息。使用Java編譯程序編譯Java代碼。在編輯完成之後,生成的描述文件是使用特定數據庫數據類型,擴展和特征性能定制的。
運行應用程序,使用SQLJ運行時程序庫和特定數據庫的JDBC驅動程序。舉例來說,如果你的主.sqlj文件定義類MyClass,那麼源文件名必須是MyClass.sqlj。編譯器生成MyClass.Java源文件,然後編譯程序生成MyClass.class類文件。而且,翻譯器和編譯程序兩者都生成profile - key類,MyClass_SJProfileKeys.class。翻譯器根據你怎樣聲明它們來命名迭代程序類與連接上下文類。舉例來說,如果你聲明一個迭代程序,MyIter,將生成一個MyIter.class類文件。
下面是配置SQLJ的必要條件∶
SQLJ運行時程序庫
JDBC驅動程序,例如:JDBC/ODBC橋,Oracle JDBC/OCI驅動程序,Oracle瘦JDBC驅動程序,DB2 JDBC驅動程序等等。
SQLJ程序將執行的JVM
現在讓我們比較SQLJ和JDBC,並比較SQLJ/JDBC和PL/SQL。
SQLJ 與JDBC
SQLJ開發的目的是完善動態JDBC SQL結構化查詢語言模型和靜態的SQL結構化查詢語言模型。與ODBC和JDBC動態模型不同,靜態模型提供強類型應用程序翻譯時間檢查。這些不僅要求進行SQL語法的編譯時檢驗和SQL語句使用的主機變量的類型兼容,而且查詢本身的正確性與數據庫模型中的表,視圖,存儲過程等等的定義有關。因為所有的SQL語句都要被編譯,SQLJ可以作為一個性能更好的中間媒質。
SQLJ代碼與JDBC代碼 下面是相同的SELECT語句的JDBC代碼碎片:
String vName; int vSalary; String vJob; Java.sql.Timestamp vDate;
...
PreparedStatement stmt = connection.prepareStatement(
"SELECT Ename, Sal " +
"INTO :vName, :vSalary " +
"FROM Emp " +
"WHERE Job = :vJob and HireDate = :vDate");
stmt.setString(1, vJob);
stmt.setTimestamp(2, vDate);
ResultSet rs = stmt.executeQuery();
rs.next();
vName = rs.getString(1);
vSalary = rs.getInt(2);
rs.close();
我們可以看到,直接在一個Java程序中嵌入SQL語句能夠生成比JDBC更加簡明易讀的代碼。因此,SQLJ在Java應用程序有數據庫訪問需要的時候,減少了開發時間和維修代價。SQLJ程序可以在同一個源文件中很容易地與JDBC代碼相互作用來做到動態的SQL語句調用,或者你也可以在SQLJ語句中使用PL/SQL語句塊來完成這個目的。此外,Oracle 9i增加了在SQLJ代碼直接支持動態SQL的功能。
Java ( SQLJ和JDBC)與Oracle數據庫中的PL/SQL比較:
Oracle數據庫應用程序中的使用的Java還不能夠替代PL/SQL。Java和PL/SQL相輔相成,Java ( SQLJ/JDBC)有下列優於PL/SQL的地方:Java能夠提供重要的性能優勢,Java存儲過程要快5到100倍,這主要取決於程序中使用的數學操作符和數據類型。理論上說,Java存儲過程可以很容易的轉化成運行在其它數據庫上的存儲過程。Java程序可以在一個復雜的應用程序的任何一層上配置∶在客戶端上,在中間層的應用程序服務器上或者在數據庫服務器本身中。Java ( SQLJ/JDBC)也同樣“分享”了PL/SQL的不足之處:PL/SQL與Oracle數據庫服務器緊密地結合起來,Oracle在近20年的時間中不斷的改進PL/SQL,而Java只在1998年的時候才被引進Oracle 8i。PL/SQL數據類型等價於Oracle本地數據類型,所以不需要進行數據類型的換算。在另一方面,JDBC提出在Java代碼和SQL語句之間插入一個普通的層,而SQLJ是又一個層。PL/SQL工作性能比Java好,因為是以數據庫為中心編程:PL/SQL存儲過程比Java程序快1.5倍(對於OLTP聯機事務處理)到2.5倍(用於批處理)。此外,Java程序要使用比PL/SQL更多的CPU資源。CPU額外開銷的增加可能是因為要進行一個比較長的編碼過程以及從Oracle到Java的額外的數據類型轉換。一個兩者兼顧達到最好效果的解決方案
Oracle提供了一個理想的環境用於利用PL/SQL和Java語言兩者的優點。在一方面,PL/SQL程序可以調用SQLJ和JDBC存儲過程,允許你構建基於組件的EJB和CORBA應用程序。現有的Java類庫可以很容易地被利用,並通過使用PL/SQL調用規范來整合入數據庫代碼開發過程中。在另一方面,Java程序可以通過JDBC或者SQLJ調用PL/SQL存儲過程,函數和匿名的程序塊。我下面想一一詳細介紹,SQLJ提供用於調用下面這些內容的語法:
存儲過程:使用CALL操作符調用UpdateSalary程序∶
#sql { CALL UpdateSalary };
函數:<0} {0>to call the GetName() function using the VALUES Operator: <}0{>使用VALUES操作符調用GetName()函數∶<0}
{0>String name; <}0{>String name;<0}
#sql { name = { VALUES GetName() };
or by using the SET Operator:
String name;
#sql { SET :name = GetName() };
{0>Anonymous PL/SQL blocks: <}0{>匿名的PL/SQL程序塊∶<0}
#sql { [DECLARE ...] BEGIN ... END; };
需要注意的是影響決定使用什麼語言的因素,不僅由執行效果決定--說得更精確些,一個現今應用程序開發過程中的主要因素,而且由程序員勞動生產率、可獲得的專家的意見和輕便性決定。幸虧對於數據庫開發人員,不必在幾種語言中選來選去,你可以很容易地把Java ( SQLJ和JDBC)與PL/SQL存儲程序混合搭配進一個數據庫來取得一個兩者兼顧達到最好效果的解決方案。
SQLJ語言元素
SQLJ是正在發展的工業標准語言,允許你使用獨立於數據庫代碼的Oracle存儲過程,可以很容易地移植到其他的可使用Java的數據庫平台。知道連接上下文,迭代程序,可執行語句和主表達式的情況,那麼你就可以把一些實際的SQLJ語句用到現實的應用程序中。
在前文中,我描述了SQLJ是什麼,比較了PL/SQL和JDBC,並且研究了SQLJ的好處。 在本文中,我將研究一下SQLJ編程語言的基礎,這樣你就可以在現實的應用程序中使用SQLJ了。SQLJ程序是一個使用嵌入式結構化詢問語言語句的規則的Java程序,以一個#_sql標記開始並以一分號結束。
有二類SQLJ語句∶聲明和可執行語句。聲明語句聲明了連接上下文和迭代程序。連接上下文用來建立數據庫連接,而迭代程序被用來存儲由SQL查詢返回的結果集;可執行語句執行嵌入式結構化詢問語句和PL/SQL程序塊。因為SQLJ程序將要被翻譯然後通過JDBC運行,任何JDBC驅動程序支持的SQLJ語句可能內嵌在一個SQLJ可執行語句中。可執行語句可能同時包含主表達式,在Java程序和數據庫之間通過Java變量交換信息。 Oracle JDBC驅動程序
Oracle提供下列JDBC驅動程序∶
客戶端瘦驅動程序是一個100%純Java驅動程序,用於在沒有安裝Oracle的客戶端。Oracle推薦使用小應用程序。當Java小應用程序運行的時候,它可以下載到浏覽器中。
OCI驅動程序( OCI8和OCI7)是用於安裝了Oracle客戶端程序的客戶端。Oracle JDBC OCI驅動程序通過調用Oracle調用界面( OCI)直接從Java訪問數據庫,提供與不同版本的Oracle7,Oracle8和8i之間最大的兼容性。這些驅動程序要求Oracle客戶程序安裝Net8。服務器端瘦驅動程序提供了與客戶端瘦驅動程序相同的函數,但是在一個Oracle數據庫內運行並且訪問一個遠程數據庫。這對於從一個擔任中間層的Oracle服務器上訪問遠程Oracle服務器是很有用的,或者,更簡單一些來說,從一個層內訪問另一個Oracle服務器,例如從某一個Java存儲過程或者EJB內訪問Oracle服務器。
服務器端內部驅動程序,稱為KPRB(Kernel Program Bundled),提供對任何在執行SQL操作的目的Oracle數據庫內運行的Java代碼的支持。服務器端內部驅動程序允許JServer JVM直接與SQL引擎通信。這是一個默認的用於在Oracle 8i/9i服務器上運行SQLJ代碼的JDBC驅動程序,這些SQLJ代碼用作存儲過程,存儲函數,觸發器,EJB或CORBA對象。KPRB JDBC驅動程序非常輕便但是效率高,並且在Oracle JServer內運行特別地能夠做到盡善盡美。這就是我們要來寫SQLJ存儲過程所用的驅動程序。讓我們來研究一下描述的SQLJ元素,逐一介紹:連接上下文,迭代程序,可執行語句以及主表達式。
連接上下文用於單一連接,你可以使用DefaultContext類的一個實例並在構造DefaultContext對象時指定數據庫URL,用戶名和口令。這是使用Oracle公司提供的oracle.sqlj.runtime.Oracle類的c
您正在看的SQLserver教程是:用SQLJ開發數據庫。onnect()方法的最容易的方法。在本例中,我們將要使用JDBC瘦驅動程序以及用戶名"scott"和口令"tiger"來通過端口1521連接MYSERVER服務器上的數據庫,在這個服務器裡,數據庫的SID是ORCL∶
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驅動程序,但是不同的模式。你可以通過使用為每個語句指定連接模式的方法在兩個不同的模式中執行SQL操作。
#sql [myContext1] { SQL statement };
...
#sql [myContext2] { SQL statement };
在這個程序的結尾,我們需要關閉在FINALLY子句和TRY/CATCH語句塊中的連接。
finally
{
#sql [myContext1] { commit };
myContext1.close();
#sql [myContext2] { commit };
myContext2.close();
}
...
迭代程序
在SQLJ程序中,SQL查詢返回的結果集可以作為一個迭代程序對象,用來保存數據。迭代程序對象是迭代程序類的一個實例,並且在概念上類似於PL/SQL游標。我們必須執行下面的五個步驟來使用迭代程序處理SQL查詢返回的數據。
聲明迭代程序類。
從迭代程序類聲明一個迭代程序對象。
使用SELECT語句添加迭代程序對象。
從迭代程序對象中讀取數據。
關閉迭代程序對象。
有二種類型的迭代程序類∶
命名的迭代程序,在這種類型中,Java變量類型和迭代程序列名都必須被指定。
位置迭代程序,在這個類型中,只有用於從數據庫中檢索得到的列的Java變量類型需要指定。
命名迭代程序∶一個命名迭代程序聲明指定了列存取器名和它們的Java類型。
讓我們來演示一下使用這個例程的五個步驟,在本例中我們想檢索雇員表Emp中的Ename,Job和HireDate列,查找工資大於1500的雇員。
聲明迭代程序類∶
#sql iterator EmpIteratorClass(String Ename, String Job, Timestamp HireDate);
我們使用Java String類來表示Ename和Job列,因為它兼容Oracle VARCHAR2數據庫類型。
java.sql.Timestamp類型用於HireDate列( Oracle的日期類型),因為java.sql.Date類型只能保存年份,日期和時間信息,而不能象Java.sql.Timestamp一樣保存小時,分鐘和秒。
從迭代程序類中聲明迭代程序對象∶
EmpIteratorClass empIterator;
使用SQL SELECT語句添加迭代程序對象。
下列SQLJ語句把Emp表的Ename,Job和HireDate列中的內容加入empIterator對象∶
int salary = 1500;
#sql empIterator = {
select Ename, Job, HireDate
from Emp
where Sal > :salary
};
我們還要聲明主變量salary,用於WHERE子句中來標識要從Emp表中返回什麼數據。
記住,通過SQL查詢返回的數據庫列的名稱必須對應於第一步中定義迭代程序列名。從迭代程序對象中讀取數據。因為迭代程序對象可能包含多個行,所以我們需要使用一個循環來訪問每一行數據,就象我們從PL/SQL游標中讀取數據一樣。命名迭代程序實現一個next()方法,允許你在迭代程序對象的數據之間移動。此外,SQLJ還提供了檢索迭代程序列的值得存取方法。下列代碼在一個循環中打印出雇員姓名,職務和雇用日期。
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();
關閉迭代程序對象∶<0}
empIterator.close();
程序段一把從第2步到第5步聯結起來演示了使用了命名迭代程序和empSalary參數的listEmployees ()方法。位置迭代程序∶與命名迭代程序相反,位置迭代程序只是指定了列的數目和種類,而不是它們的名稱。列數據可以只通過位置訪問,通過傳統的FETCH... INTO語法。除了使用FETCH語句之外,還需要使用位置迭代程序方法endFetch ()來檢測結束條件從而從循環中跳出。但是必須在訪問所需要取得的數據之間檢查條件。
下面是相同的五個步驟,演示如何使用位置迭代程序:
聲明迭代程序類∶
#sql iterator EmpIteratorClass(String, String, Timestamp);
從迭代程序類中聲明迭代程序對象。還必須聲明從迭代程序對象中取得的數據所必需的主變量。
EmpIteratorClass empIterator;
String name = null;
String job = null;
Timestamp hireDate = null;
使用SQL SELECT語句加入迭代程序對象∶
int salary = 1500;
#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());
}
關閉迭代程序
您正在看的SQLserver教程是:用SQLJ開發數據庫。對象
empIterator.close();
把從第2步到第5步聯結起來演示了使用了位置迭代程序和empSalary參數的listEmployees ()方法。可見,位置迭代程序對象所使用的語法很像PL/SQL游標的語法。命名迭代程序和位置迭代程序兩者都執行基本相同的功能∶它們都保存SQL查詢的結果,有可能還是很多行數據至於到底使用哪一種迭代程序則要看情況或是根據自己的偏愛;從性能角度上來看呢,它們產生的結果都是一樣的。
可執行語句
可執行的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} };
or
#sql { SET :salary = getSalary(:empNo) };
主機表達式
在上面的例子中,我們可以看到,主機變量允許SQLJ程序在數據庫和Java程序之間交換信息。它們是任何在Java程序中聲明的變量。主機變量被嵌入到SQLJ語句裡,稱作主機表達式。主機表達式把主機變量綁定在SQLJ可執行語句上,它們也可能包括Java數組元素,對象屬性或者Java函數。SQLJ負責在SQL和Java環境之間來回移動數據。所有的標准JDBC類型-象Boolean,byte,short,int,String,byte [],double,float,Java.sql.Date等等。—都是SQLJ中有效的主機表達式。此外,Oracle的SQLJ翻譯器支持使用Oracle數據庫類型,例如ROWID,CLOB,BLOB和Object以及REF類型。在本文中,我討論了編寫實際的SQLJ代碼所必需用到的SQLJ對象類型:連接上下文,命名和位置迭代程序,可執行語句和主機表達式。在以後的文章裡,我想編寫一個服務器端SQLJ程序,編譯它,然後把它配置進Oracle JServer並把它PL/SQL對應的程序進行性能上的比較。
代碼段1
public static void listEmployees(String empSalary)
throws SQLException {
EmpIteratorClass empIterator;
Integer salary = new Integer(empSalary);
try {
#sql empIterator = {
select Ename, Job,
HireDate
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);
}
}