利用Java預存程序溝通SQL、XML、Java、J2EE和Web服務。
預存程序(stored procedure)允許將運行於資料庫層中的持久性邏輯與運行於中介層中的商務邏輯有效地分離開來。這種分離可以降低整個應用程式的複雜性,並提供其重用性、安全性、效能和延展性。
但是,妨礙預存程序廣泛採用的一個主要障礙是不同資料庫廠商使用各種專有的、且依賴於資料庫的實現語言。使用基於Java的預存程序可以解決這一問題。Oracle已經實現了ANSI標準,這些標準規定了從SQL中將靜態Java方法作為過程或函數進行調用的能力。這種實現被簡單地稱作"Java預存程序"。
在本文中,你將瞭解基於Java的預存程序如何協助簡化商務邏輯、提高其效能,並擴充資料庫的功能。本文將介紹Oracle如何在資料庫內啟用基於Java的預存程序。還會介紹Java預存程序如何訪問資料,以及如何建立基本Java預存程序。
選擇PL/SQL還是Java
在考慮Oracle預存程序時,你可能會想到PL/SQL。不過,從Oracle8i開始,Oracle已經在資料庫中支援Java,從而為預存程序提供了不同於PL/SQL的開放式和可移植的方法。我可以聽到"$64 000問題":"我如何在PL/SQL和Java之間做出選擇?我是否應當忘記已經學習的所有PL/SQL相關知識,而變為一個Java天地的新手?"
兩種語言都適用於資料庫編程,都有自己的優點和弱點。在決定選擇哪一種語言時,可以參考下面根據經驗得出的通用規則:
- 對於要求與SQL進行無縫整合的資料庫中心來說則邏輯使用PL/SQL,從而完成對資料庫物件、類型和特性的訪問。
- 出於與資料庫的無關性考慮時,可以選擇Java作為開放式的語言來取代PL/SQL,同時也為了整合和溝通SQL、XML、J2EE和Web服務等各個領域。
OralceJVM使得Java可以運行在資料庫中
從Oracle8i版本1(Oralce8.1.5)開始,Oracle便提供緊密整合的Java虛擬機器(JVM),JVM支援Oralce的資料庫會話期結構。任何資料庫對話期都可以在第一Java代碼調用時啟動一個虛擬上專用的JVM,後續的使用者可以使用這一已經存在的支援Java的會話期。事實上,所有會話共用同一JVM代碼並保持"僅靜態"的私人狀態,而垃圾則收集在單個對話期空間內,從而為各個Java對話期提供了和SQL操作相同的對話期隔離和資料完整性能力。這裡,不需要為了資料完整性而進行單獨的Java支援的過程。這一基於對話期的結構提供了較小的記憶體佔用率,並使OracleJVM具有與Oracle資料庫一樣的線性SMP延展性。
建立Java預存程序
要將Java方法轉換為Java預存程序需要幾個步驟,包括:用loadjava公用程式將Java類載入到資料庫中,利用調用規範(Call Spec)發布Java方法,將Java方法、參數類型和傳回型別映射到其SQL的對應部分。下面部分說明如何完成這些步驟。
我將使用一個簡單的Hello類,它有一個方法Hello.world(),返回字串"Hello world":
public class Hello{ public static String world () { return "Hello world"; }}
Loadjava 公用程式
Loadjava是載入Java源檔案、Java類檔案和Java資源檔的公用程式,它可以用來驗證位元組碼,並將Java類和JAR檔案布置到資料庫中。它既可以通過命令列調用,也可以通過包含於DBMS_JAVA類中的loadjava()方法調用。為了載入我們的Hello.class樣本,輸入:
loadjava -user scott/tiger Hello.class
從Oracle9i版本2開始,loadjava允許通過為包含在被處理的類中的方法建立相應的Call Specs來自動將Java類發布為預存程序。Oracle為開發、測試、調試和布置Java預存程序提供了Oracle9i JDeveloper。
The Resolver Spec
基於JDK的JVM在列於CLASSPATH中的目錄中尋找類引用,並對其進行解析。因為Oracle資料庫類存在於資料庫模式中,所以OracleJVM利用資料庫解析器(resolver)通過列於Resolver Spec中的模式尋找並解析類引用。與CLASSPATH不同(CLASSPATH可以應用於所有的類),Resover Spec根據每類的情況進行應用。預設解析器首先在載入類的模式中搜尋類,然後在公用同義字(public synonyms)中搜尋。
loadjava -resolve <myclass>
你可能需要指定不同的解析器,也可以在使用loadjava時強制進行解析,從而在布置時確定可能在以後運行時發生的任何問題。
loadjava -resolve -resolver "((* SCOTT) (foo/bar/* OTHERS) (* PUBLIC))"
Call Spec和預存程序調用
為了從SQL中調用Java方法(以及從PL/SQl和JDBC中調用),必須首先通過Call Spec發布公用靜態方法,它為SQL定義方法採用的參數以及返回的SQL類型。
在我們的例子中,我們將利用SQL*Plus串連到資料庫,並為Hello.world()定義一個頂級Call Spec:
SQL> connect scott/tigerSQL> create or replace function helloworld returnVARCHAR2 as language java name 'Hello.world () returnjava.lang.String'; /Function created.
可以像下面這樣調用Java預存程序:
SQL> variable myString varchar2[20];SQL> call helloworld() into :myString;Call completed.SQL> print myString;MYSTRING---------------------Hello world
Java預存程序可以通過其Call Spec從以下各項中進行調用:SQL DML語句(INSERT, UPDATE、DELETE、SELECT、CALL、EXPLAIN PLAN、LOCK TABLE和MERGE)、PL/SQL塊、子程式、程式包以及資料庫觸發器。Call Spec的美妙之處在於預存程序實現可以從PL/SQL轉換為Java,反之亦可,這一點對於要求者是透明的。
Call Spec從實現語言中(PL/SQL或Java)中抽象出調用介面,因而使之能夠在原有應用程式和新的基於Java/J2EE的應用程式之間共用商務邏輯。但是,在從Java客戶程式調用在資料庫駐留的Java類時,你可能不希望通過PL/SQL封裝器(wrapper)。在以後的版本中,Oracle計劃提供一種機制,它可以使開發人員略過Call Spec。
進階資料存取控制
Java預存程序可用於控制和限制對Oracle資料的訪問,其方法是只允許使用者通過預存程序管理資料,而預存程序在其調用者的許可權內執行,而不能對錶本身進行訪問。例如,你可以在特定時間內禁止更新資料,或者使管理者只具有查詢工資資料的權利,而不能進行更新,或者記錄所有的訪問並通知某一安全機構。
原有應用程式與J2EE應用程式之間的資料邏輯共用
因為原有應用程式與J2EE應用程式都通過Call Spec調用預存程序,所以J2EE和非J2EE應用程式可以共用相同的資料邏輯。由於有了Call Spec,所以不用考慮所用的是何種實現語言(無論是PL/SQL還是Java),該資料邏輯都可以共用。
為BMP實體Bean自動產生主關鍵字
在對EJB實體bean應用BMP時,一個bean執行個體可以由自動產生的與新插入的資料相關聯的主關鍵字惟一確定,它是ejbCreate()的傳回值。可以利用一個插入相應資料的預存程序在一個資料庫操作中檢索ejbCeater()中的該值,並檢索或計算主關鍵字。作為另一種方法,也可以利用JDBC3.0的RETURN_GENERATED_KEYS特性,以一個SQL語句插入該資料並檢索相應的關鍵字(或ROWID)。但是,預存程序方法在各個JDBC磁碟機版本和資料庫之間更具可移植性。
可以用以下三個步驟實現這一模式:
- 建立一個Java預存程序,在公用GenPk類中定義一個公用靜態Java方法insertAccount()。此方法將插入資料、計算惟一的關鍵字(通過發出一個序號),並返回計算出的關鍵字作為主關鍵字。
- 定義Call Spec
CREATE OR REPLACE PROCEDURE insertAccount(owner INvarchar, bal IN number, newid OUT number)AS LANGUAGE JAVA NAME 'GenPK.insertAccount(java.lang.String [])';/
- 在ejbCreate()內調用預存程序
Public AccountPK ejbCreate(String ownerName, int balance) throws CreateException{ try { CallableStatement call = conn.prepareCall{ "{call insertAccount(?, ?, ?)}"}; return new AccountPK(accountID); }}
為CMP實體Bean定製主關鍵字尋找器
尋找器方法(Finder methods)用於檢索已存在的EJB實體bean執行個體。主關鍵字尋找器使你能夠檢索惟一標識的EJB執行個體。對於CMP實體bean,EJB容器根據聲明描述,自動產生主關鍵字尋找器findByPrimaryKey()方法。但是,在某些情況下,可能需要更多的控制,例如可能需要專門的尋找器,如findByStoredProcKey()。在這些情況下,你可以結合使用Java預存程序和對象關係架構(如Oracle9i應用伺服器[Oracle9iAS] TopLink)來實現定製的主關鍵字尋找器方法。在將EJB尋找器定義為REDIRECT或NAMED尋找器後,TopLink將產生一個SQL查詢用於檢索bean執行個體。
資料驅動的EJB調用
在資料驅動體繫結構中,商務邏輯調用可以作為資料庫操作(如插入、更新或刪除)的結果來觸發。實現該資料邏輯的Java預存程序可以被聲明為資料庫觸發器,用以調用運行於中介層J2EE應用伺服器的EJB。EJB的調用既可以採用J2EE1.3相容的伺服器通過Interoperable Inter-ORB Protocol(IIOP)標準遠程方法調用(remote method invocation,RMI)實現,也可以通過銷售商特定的傳輸協議(如Oracle9iAS/Oc4J的ORMI,或者通過BEA WebLogic的T3)用RMI來實現。每個應用伺服器供應商在提供基於IIOP的RMI,以提供互通性的同時,都有其自己最佳化的協議。Oracle9iAS同時支援基於IIOP的RMI調用和基於ORMI協議的RMI調用。
資料驅動的訊息傳送
Oracle9i資料庫嵌入了Advanced Queuing(AQ,進階排隊),它是一種整合的、穩定、可靠、安全、可擴充和交易處理式的訊息排隊架構。Oracle通過標準的Java訊息傳送系統(Java Messaging System,JMS)API為Java開發人員提供AQ功能。Java預存程序可以通過JMS介面調用AQ操作,從而能夠實現快速、在會話期內、可擴充的、資料驅動的訊息傳送。
Java預存程序可以利用JMS調用AQ操作。可以用以下4個步驟實現這一模式:
- 建立並啟動JMS Queue(為此,可以將以下一些操作嵌入SQL指令碼內):
execute dbms_aqadm.create_queue_table(queue_table =>'queue1', queue_payload_type =>'SYS.AQ$_JMS_TEXT_MESSAGE', comment => 'a test queue', multiple_consumers => false, compatible => '8.1.0');execute dbms_aqadm.create_queue( queue_name => 'queue1', queue_table => 'queue1' );execute dbms_aqadm.start_queue(queue_name => 'queue1');
- 建立Java預存程序(代碼摘錄如下):
public static void runTest(String msgBody){ try { // get database connection ora_drv = new OracleDriver(); db_conn = ora_drv.defaultConnection(); // setup sender (cf online code sample) .. // create message s_msg = s_session.createTextMessage(msgBody); // send message sender.send(s_msg); s_session.commit(); // receive message r_msg = (TextMessage) receiver.receive(); r_session.commit(); // output message text String body = r_msg.getText(); System.out.println("message was '"+body+"'"); ..}}
- 建立Call Spec:
create or replace procedure jmsproc (t1 IN VARCHAR) as language java name 'jmsSample.main (java.lang.String[])';/
- 調用預存程序:
call jmsproc('hello');
資料庫輔助的Web發布(緩衝失效)
各應用程式結構必須面對的一個共同問題是如果可靠地將資料庫資訊進行緩衝,以提高整個系統的效能。JCACHE是一種即將公布的標準規範(JSR 107),它可以解決這一問題。它說明了一種對Java對象臨時在記憶體中進行緩衝的方法,包括對象的建立、共用訪問、假離線(spooling)、失效、各JVM的一致性等。它可被用於緩衝JSP內最經常讀取的資料,如產品目錄和價格列表。利用JCACHE,多數查詢的反應時間會因為有緩衝的資料而加快(自我裝載表明反應時間大約快15倍)。
為了跟蹤未經處理資料的所有變化,並重新整理已緩衝的資料,Java預存程序會作為一個觸發器被附加在一個表上。這個表的任何變化都會自動調用該預存程序,後者再調出一個已定義的JSP使JCACHE對象失效,該對象將其狀態映射到該資料庫表。在失效時,緊跟其後的查詢將強制緩衝器根據資料庫的資料進行更新。
擴充資料庫的功能
在資料庫中直接運行Java代碼的一個妙處就在於要實現新的功能,只需要簡單地載入代碼或庫,並利用Call Spec製作可用於SQL、PL/SQL、Java、J2EE和非Java API的進入點(公用靜態方法)。Oracle9i資料庫使用者可以很容易地擴充資料庫功能。Oracle自己利用這種能力來獲得新的應用程式和工具包,如XML Developer Kits(XDKs)。
溝通SQL、PL/SQL、Java、J2EE、.NET和XML
Oracle XDK是用Java編寫的,並將其公用方法可用作Java預存程序,從而擴充了資料庫的XML可程式化能力。SQL、PL/SQL、Java、J2EE和非Java(.NET)商務邏輯都能夠訪問XML分析器、XSLT處理器、XPath引擎和XML SQL Utility(XSU)。
XML分析器可以通過xmlparser和xmldom包進行訪問。XSU是一種Java公用程式,它可以由SQL查詢結果或JDBC ResultSet產生XML文檔,並將XML文檔中的資料寫入資料庫表或視圖中。利用XSU,XML輸出可以輸出為文本、Dom樹或DTS。通過dbms_xmlquery和dbms_xmlsave包,XSU即可用於PL/SQL。
結論
Oracle資料庫與Java VM的整合可以建立可移植、功能強大和與資料庫無關的資料邏輯和持久性邏輯(persistence logic)。運行於中介層的商務邏輯和運行於資料庫層的資料邏輯之間的分離提高了應用程式的可擴充性、靈活性和可維護性。