source Java_JAVA SOURCE (1)
用Java開發(fā)Oracle存儲過程
作者:Michael Klaene
譯者:玄機逸士
從Oracle 8i開始,Oracle數據庫就全面引入了實用的Java虛擬機– Oracle JVM。Oracle和Java之間的這種緊密關系,使得一個非常重要的技術,出現在Oracle開發(fā)人員面前:Java存儲過程。通過Java存儲過程,開發(fā)人員在創(chuàng)建數據庫應用的時候,就可以充分利用Java提供的各種優(yōu)勢。本文將針對這個越來越受歡迎的技術進行討論。其目標讀者是,初懂Oracle的Java開發(fā)人員和略知Java的Oracle PL/SQL開發(fā)人員。在強調Java存儲過程的好處之后,我將示例說明在應用開發(fā)中,如何使用它們。
Java存儲過程的好處
在很多情況下使用Java存儲過程是很有意義的。鑒于Java當前受歡迎的程度,這種情況是完全可能的,即越來越多的開發(fā)人員對于Java的熟練程度要好于PL/SQL。Java存儲過程的出現,使得Java的開發(fā)人員可以用自己喜歡的語言來開發(fā)存儲過程。對于有經驗的PL/SQL開發(fā)人員,則可以利用Java語言的各種優(yōu)點,來擴展數據庫應用的功能。同時,Java使得編寫獨立于數據庫的代碼成為可能。更有意思的是,它允許你重用你已經存在的代碼,從而大幅度地提高開發(fā)效率。
正如你將看到的,PL/SQL和Java可以在同一個應用中和諧共處,因此,大可不必非此即彼地選擇其中之一。PL/SQL是一種針對Oracle數據庫,經過高度優(yōu)化的、優(yōu)秀的過程語言,Java應用在Oracle數據庫中運行也具有很好的擴展性。除此之外,通過OracleJVM來執(zhí)行Java程序,可以充分利用高效的內存回收技術和線程管理方面的能力。
Java存儲過程,Step by Step
簡單地說,Java存儲過程就是Java類,以schema對象的形式存儲,通過調用規(guī)范(call specifications),OracleSQL和PL/SQL可以對其訪問。我們將看到,調用規(guī)范就是一些簡單的PL/SQL聲明,這些聲明’包裝(wrap)’了存儲在數據庫中的Java方法。開發(fā)Java存儲過程,有四個必須的步驟。下面我們逐一來看看這些步驟。
#1.編寫Java類
第一步的妙處就是,它基本上和Oracle數據庫沒有什么關系。你只是簡單地用你最喜歡的IDE,比如Oracle的Jdeveloper,去開發(fā)一些Java類。如果想被用作存儲過程,Java方法必須是public且static的。
在移入Oracle數據庫之前,你可以自由地編寫、編譯甚至對Java代碼進行單元測試。事實上,對于通常的應用,這是一個很好的方式,因為這可以充分利用IDE的特性,諸如調試和代碼生成。如果你想用Oracle的JVM來編譯Java代碼,后面將要說到的loadjava這個工具,將為你做這些工作。
下面的代碼列出了一個簡單的Java類- EmpManager。它包只含了一個簡單的,用于向數據庫插入一個emp(員工)記錄的方法。
import java.sql.*;
import oracle.jdbc.*;
public class EmpManager {
//Add an employee to the database.
public static void addEmp(int emp_id, String emp_f_name,
String emp_l_name,float emp_salary, int dept_id) {
System.out.println("Creating new employee...");
try {
Connection conn =
DriverManager.getConnection("jdbc:default:connection:");
String sql =
"INSERT INTO emp " +
"(emp_id,emp_f_name,emp_l_name,emp_salary,dept_id) " +
"VALUES(?,?,?,?,?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1,emp_id);
pstmt.setString(2,emp_f_name);
pstmt.setString(3,emp_l_name);
pstmt.setFloat(4,emp_salary);
pstmt.setInt(5,dept_id);
pstmt.executeUpdate();
pstmt.close();
}
catch(SQLException e) {
System.err.println("ERROR! Adding Employee: "
+ e.getMessage());
}
}
}
到目前為止一切如常。在這個方法中,數據庫鏈接URL是"jdbc:default:connection:"。在寫要在Oracle數據庫中運行的Java代碼時,可以利用一個特殊的服務器端的JDBC驅動程序。這個驅動程序使用用戶缺省的鏈接,并提供對數據庫的最快訪問速度。
#2.加載Java類
我們的Java類將要編程一個真正的schema對象,所以必須將它移入數據庫。為此,Oracle提供了一個命令行工具– loadjava。這個loadjava工具提供了一個SQL CREATE JAVA等語句必要的接口,并且也可以用于將Java相關的文件加載到數據庫中。
由于現在還沒有編譯EmpManager.java,我們也可以讓loadjava在加載的過程中順便做完編譯工作,這個可以通過打開–resolve開關來做到。
$ loadjava –u scott/tiger –v –resolve EmpManager.java
出–resolve開關外,-v將指示loadjava輸出詳細的(verbose)反饋,-u用于指定數據庫用戶和密碼。因為我們讓loadjava編譯源文件,因此源文件和類文件都將是SCOTT schema的成員。
我們可以通過一個對USER_OBJECTS的查詢來驗證編譯和加載的狀態(tài),如果正確,那么狀態(tài)將是’VALID’的。
SELECT object_name, object_type, status
FROM user_objects WHERE object_type LIKE ‘JAVA%’;
object_name object_type status
EmpManager JAVA CLASSVALID
EmpManager JAVA SOURCE VALID
反之,如果編譯失敗,可以在視圖USER_ERRORS中看到具體的錯誤。
如果選擇用IDE編譯,只需要簡單將編譯后的class文件加載即可,源文件可以保存在版本控制工具的文件系統(tǒng)中。Loadjava工具接受后綴為.sqlj (sqlj源文件),.properties,.ser,.jar和.zip等文件。當后綴為.jar和zip的文件時,Oracle將自動解壓,并將各成員存儲成單個的schema對象。
在繼續(xù)討論之前,加載過程中有一個至關重要的組件值得一提:Oracle JVM resolver。典型地,一個JVM用classpath來定位Java類,以便被其他程序使用。在數據庫中加載Java時,resolver來完成這樣的任務。
可以簡單地認為resolver就是Oracle版本的classpath。Oracle將核心Java類存儲與PUBLIC schema中。PUBLIC,就像你自己的schema一樣,將被自動地引入缺省的resolver中。不過,如果需要引用另外一個schema中的類,那就必須提供你自己的’resolver規(guī)則(spec)’,這個可以通過加上-resolver開關來做到。比如,loadjava –u scott/tiger@test –resolve –resolver “((* SCOTT) (* PUBLIC) (* ADMIN))”,指明了,在決定class依賴關系時,SCOTT schema,PUBLIC和ADMIN這3個schema將被搜索。
#3.發(fā)布(publish)Java類
第3步是發(fā)布Java類。Java類必須發(fā)布,以便可以直接從SQL或者PL/SQL直接訪問。通過為其創(chuàng)建、編譯一個調用規(guī)則來發(fā)布一個Java類。調用規(guī)則,通常被稱為call spec或者PL/SQL包裝器(wrapper),它將Java方法的參數、返回值類型映射成Oracle SQL的數據類型。下面給出的是addEmp方法的調用規(guī)則:
CREATE OR REPLACE PROCEDURE add_emp (emp_id NUMBER,emp_f_name VARCHAR2,
emp_l_name VARCHAR2, emp_salary NUMBER, dept_id NUMBER)
AS LANGUAGE JAVA
NAME 'EmpManager.addEmp(int, java.lang.String, java.lang.String,
float, int)';
/
add_emp過程為Java的EmpManager.addEmp方法提供了一個SQL接口。Java方法,如果有相關包名,則必須使用包含包名的全名,并且,在開發(fā)一個調用對則時,Java對象如String也必須使用全名。
作為一般性的規(guī)律,一個Java方法如果沒有返回值,則被封裝成過程,反之,則被封裝成函數。現在,在EmpManager中,我們考慮加入第2個Java方法,用于查詢一個指定部門的員工人數:
//查詢一個指定部門的員工人數
public static int getEmpCountByDept(int dept_id) {
Connection conn =
DriverManager.getConnection("jdbc:default:connection:");
String sql = "SELECT COUNT(1) FROM emp WHERE dept_id = ?";
int cnt = 0;
//Code here to add ResultSet value to cnt, trap SQLException, etc.
return cnt;
}
它的調用規(guī)則指定其返回類型為NUMBER。
CREATE OR REPLACE FUNCTION get_emp_count_by_dept (dept_id NUMBER)
RETURN NUMBER AS LANGUAGE JAVA
NAME 'EmpManager.getEmpCountByDept(int) return int';
/
缺省地,就像標準的PL/SQL過程一樣,這些代碼(譯注:指上面的調用規(guī)則)只要有INVOKER權限就可以運行他們,換言之,當前用戶有權執(zhí)行它們。通過增加關鍵字AUTHID DEFINER,可以讓其他用戶以創(chuàng)建者的身份來執(zhí)行它們。
一旦執(zhí)行,調用規(guī)則,則將其他文件作為SCOTT schema的成員加入數據庫。
#4.調用過程
我們已經開發(fā)、裝載并發(fā)布了Java類。最后一步就是執(zhí)行他們。缺省地,Java的輸出被寫入跟蹤文件(trace files)。DBMS_JAVA包,Oracle提供的用于管理服務器端Java的工具,可以將輸出重定向到SQL*Plus。
SQL> SET SERVEROUTPUT ON
SQL> CALL dbms_java.set_output(2000);
現在,只要執(zhí)行,Java的輸出會顯示在SQL *PLUS中,
SQL> EXECUTE add_emp(1,'Joe', 'Smith',40000.00,1);
Creating new employee...
PL/SQL procedure successfully completed.
正如你所看到的,從調用者的角度,調用Java存儲過程和調用PL/SQL的存儲過程或存儲函數,并沒有明顯的區(qū)別。
VARIABLE x NUMBER;
CALL get_emp_count_by_dept(1) INTO :x;
Getting Number of Employees for Dept...
Call completed.
PRINT x
X
----------
1
SQLException類有getErrorCode()和getErrorMessage()兩個方法用于報錯處理。Java存儲過程中的任何未被捕獲的異常將產生ORA-29532告警,Java調用也會被隨之終止。至于如何處理異常,則不同的應用可能采用不同的方式。addEmp方法簡單地捕獲并顯示異常。視圖插入一個員工到一個無效的部門,將會收到一個錯誤消息。
SQL> execute add_emp(2,'Tom', 'Jackson', 45000.00,2);
Creating new employee...
ERROR! Adding Employee : ORA-02291: integrity constraint
(OPS$AK4353.FK_DEPT_ID) violated -
parent key not found
由于有從PL/SQL來調用Java的需要,自然就會想到也有從Java調用PL/SQL的需要,這個可以通過在Java方法中使用CallableStatement對象,而輕易做到。
CallableStatement cstmt = conn.prepareCall("{my_plsql_proc}");
因此,可以創(chuàng)建一個無縫的環(huán)境,使得PL/SQL輕易調用Java,反之亦然。
一種使用方案
理解Java存儲過程越好,將對你的開發(fā)時間越有幫助。一個通常的方法就是,在需要考慮數據庫訪問效率時(譯注:建立數據庫連接很耗時間和資源),就會使用PL/SQL。現在Java就可以更加輕松地做到這點。開發(fā)各個類,遵照必要的調用規(guī)則即可。
或許有這樣的可能,比如一個數據庫應用需要與操作系統(tǒng)文件和目錄進行交互。Oracle UTL_FIL這個包提供訪問操作系統(tǒng)文件的功能極為有限,而Java則有豐富得多的File IO能力,允許開發(fā)人員刪除文件,增加目錄等等。所以為什么不利用它呢?再比如,命令行PL/SQL程序的使用者,也可能將job參數存放在一個配置文件中,你可以編寫Java方法來讀取這些參數:
public static String readFile (String usrFile){
String fileStr = new String();
try {
File file = new File(usrFile);
FileReader fr = new FileReader(file);
LineNumberReader lnr = new LineNumberReader(fr);
...
...
}
catch(Exception e) {
...
}
return fileStr;
}
然后,PL/SQL包會為你寫的這個或者任何其他FILE IO方法定義調用規(guī)則。
CREATE OR REPLACE PACKAGE my_java_utils IS
FUNCTION read_file (file VARCHAR2) RETURN VARCHAR2;
END my_java_utils;
/
CREATE OR REPLACE PACKAGE BODY my_java_utils IS
FUNCTION read_file (file VARCHAR2) RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'MyJavaUtils.readFile(java.lang.String) return java.lang.String';
END my_java_utils;
/
PL/SQL過程可以調用read_file這個Java存儲過程,而它則以一個文件的數據作為輸入。同時使用兩個世界最好的方法,開發(fā)人員可以輕易開發(fā)出魯棒的數據庫應用。必須注意到,在這個特定的方案中,需要有訪問文件系統(tǒng)的一定權限。更進一步的信息,可以查詢Oracle的Java安全方面的文檔。
總結
Oracle提供了很多Java存儲過程方面的文檔(譯注:可以到OTN上找)。如果需要,你可以下載本文,以便做更加細致的試驗。
在這篇文章中,給出了有關Java存儲過程的總的看法,同時示例說明了如何實現它們。Java存儲過程是一種很強大的技術,值得一個Oracle開發(fā)人員在構建數據庫應用時,考慮使用之。
總結
以上是生活随笔為你收集整理的source Java_JAVA SOURCE (1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小太阳豆浆机价格多少
- 下一篇: 镀锌板用途(铜铝复合板的用途)