現在的java的數據庫-關系映射技術似乎不提倡用存儲過程,其實存儲過程更能發揮數據庫的效率。
1? 引言
存儲過程因其執行效率高、與事務處理的結合、運行更安全等優點,在數據庫應用程序中被廣泛采用。PL/SQL是用于從各種環境中訪問Oracle數據庫的一種編程語言,它與數據庫服務器集成在一起,PL/SQL編寫的存儲過程編譯效率高,網絡系統開銷小,同時PL/SQL直觀性好,是大多數人的選擇。
以Number、Varchar等基本標量類型為輸出參數的PL/SQL存 儲過程,每個輸出參數只能返回一個對應值。而在實際數據庫應用程序中,尤其是在進行系統綜合查詢統計時,往往需要返回二維數組或結果集,這時應考慮在存儲 過程中使用集合這種數據結構。對于集合,我們可以一次把許多元素作為一個整體進行操作,也可以對集合中的單個元素進行操作,使用方便、靈活。
?
2? PL/SQL存儲過程及Java程序的編寫
2.1??索引表作為輸出參數
索引表是無約束的,其大小的唯一限制(除可用內存外)就是它的關鍵字BINARY_INTEGER類型所能表示數值的約束(-2147483647...+2147483647),其元素不需要按任何特定順序排列。在聲明時,我們不需要指定其大小,而且對索引表的元素可以直接賦值,不用初始化,可見使用索引表極其方便。
2.1.1
存儲過程的編寫
我們可以在PL/SQL語句塊中定義索引表,但作為輸出參數的索引表,必須要在包(package)里定義,方法如下:
create or replace package out_param is?????????????????????????????????????????
----?定義了元素是varchar2類型的一個索引表類型
type out_index_table_typ is table of varchar2(50) index by binary_integer;
end out_param;
接下來就可以在pl/sql存儲過程里引用在包里定義的索引表類型:
????????????????? ?create or replace procedure testPro1(in_param in varchar2,o_table out? out_param. out_index_table_typ ) is
????????????????????? begin?
????????????????? ????????-------這里略去程序體
????????????????????? end? testPro1;?
??????????其中,返回的索引表類型前必須用包名加上句點來引用out_param. out_index_table_typ?
2.1.2? J
ava程序的編寫
索引表與數據庫表很形似,有key和value兩列,但它不是真正的數據庫表,不可以存儲到數據庫中。因此索引表不能使用SQL進行操作,這樣它的內容不能通過一個標準的SELECT語句返回游標得到。這一點與嵌套表有很大不同。由存儲過程返回的索引表可以映射成java數組類型、
JDBC Datatypes的
BigDecimal[]數組類型和oracle的Datum[]數組。有一點要注意,盡管索引表中的元素不一定要按任何特定順序排列,其元素可以借助于任意有效關鍵字而插入,但對映射數組元素的引用應該從1開始,而且要連續,否則映射成數組時會出現null元素。
下面示例為將索引表映射成java數組類型。
- import ?oracle.jdbc.*;??
- import ?oracle.sql.*;??
- import ?java.sql.*;??
- public ? class ?ReturnIndexTable??
- {??Connection?ociconn= null ;??
- OracleCallableStatement?stmt?= null ;??
- public ?String[]?getTable(String?in_param)??
- {??String[]?reAry= null ;??
- try ??
- {??OracleDriver?S_Driver= null ;??
- if (S_Driver== null )??
- S_Driver= new ?oracle.jdbc.OracleDriver();??
- DriverManager.registerDriver(S_Driver);??
- String?url= "jdbc:oracle:oci8:@test" ;??
- String?user= "user" ;??
- String?password= "?password" ;??
- ociconn=?DriverManager.getConnection(url,user,password);??
- stmt?=(OracleCallableStatement)ociconn.prepareCall( "begin?testPro1(?,?);?end;" );??
- //?返回的索引表最大長度(可以大于索引表實際長度) ??
- int ??maxLen?= 31 ;??
- //?索引表元素類型 ??
- int ??elemSqlType?=?OracleTypes.VARCHAR;??
- //索引表元素長度(CHAR,?VARCHAR?or?RAW),其它元素類型可忽略該項值,但該參數仍須定義 ??
- int ??elemMaxLen= 50 ;??
- stmt.setString( 1 ,in_param);??
- //?注冊返回參數 ??
- stmt.registerIndexTableOutParameter( 2 ,maxLen,elemSqlType,elemMaxLen);??
- stmt.execute();??
- //?返回數組類型 ??
- reAry=(String[])stmt.getPlsqlIndexTable( 2 );??
- }??
- catch ?(Exception?e)??
- {e.printStackTrace();??
- }??
- finally ??
- { return ?reAry;??
- }??
- }??
- //關閉連接............. ??
- }??
?
2.2??可變數組作為輸出參數
????????? ?可變數組和另外兩種集合類型不同,其元素在內存中是連續存儲的,且在大小方面有一個固定的上界。聲明時需要指定該數組中元素的最大數目(可變數組的大小可以用EXTEND方法來增加,但不能被擴展超過所聲明的極限大小)。
可變數組的元素被賦值之前,必須使用構造器進行初始化。元素插入數組時應從索引1開始,連續插入。
2.2.1
?存儲過程的編寫
可變數組的定義方法如下:
create or replace type testArray is varray(5) of number(3)
???????? PL/SQL存儲過程里調用可變數組作為輸出參數:
create or replace function getTestArray return testArray
as
???o_data testArray:= testArray (); ?
begin
??? for v_count in 1..5 loop
????? o_data.extend;?
????? o_data(v_count):= v_count;
??? end loop;
???return o_data;
end;?
2.2.2? J
ava程序的編寫
????????
由存儲過程返回的可變數組同樣可以映射成java數組類型。但Java程序調用存儲過程返回的可變數組方式和索引表方式卻不相同,這一點應注意,具體方法如下:
public static void main( )?
{
......
//調用存儲過程
.????????? ?OracleCallableStatement stmt =(OracleCallableStatement)conn.prepareCall ( "begin ? := getTestArray; end;" );????
??
??????????stmt.registerOutParameter( 1, OracleTypes.ARRAY,"?testArray" );
? ??????????stmt.executeUpdate();
?? ?????????// 得到 ARRAY 對象
?? ?????????
ARRAY simpleArray = stmt.getARRAY(1);
//轉換為java數組
? ??????????
String[ ] values = (String[])simpleArray.getArray();
//輸出數組內容
?? ?????????for( int i = 0; i < values.length; i++ )
??? ?????????System.out.println( "row " + i + " = '" + values[i] +"'" );
}
2.3??嵌套表作為輸出參數
存 儲過程中使用嵌套表,并不是直接將嵌套表作為輸出參數,而是對嵌套表"造型"后以游標形式輸出。嵌套表的基本功能與索引表相同,但嵌套表可以使用SQL進 行操作,其內容可通過SELECT 語句查詢并"造型"后以游標形式返回。在大多數的查詢統計中,常常需要返回結果集,這時使用嵌套表就尤其方便。筆者在開發過程中深刻體會到使用對象嵌套表 可以解決絕大多數的查詢統計問題。下面著重介紹如何在存儲過程里利用對象類型的嵌套表。
對于任意的統計分析表格,我們可以將其簡化成下面的輸出形式:
??? | ??????統計項目1 | 統計項目2 | 統計項目3 | 統計項目4 |
名稱1 | ? | ? | ? | ? |
名稱2 | ? | ? | ? | ? |
這樣我們把每一行看作是一個對象實例,該行的每一列則可以看作是該對象的一個屬性,下面通過構造對象,然后對包含對象的嵌套表進行造型,返回游標得到結果集。
?2.3.1?
存儲過程的編寫
??首先構造統計對象如下:
????? create or replace type TestObj as object
(?
?vname? varchar2(20),??? --名稱
?item1?? number,??????? --統計項目1
?item2?? number,??????? --統計項目2
?item3?? number,??????? --統計項目3
item4?? number??????? --統計項目4
);
構造包含對象類型的嵌套表:
?create or replace type TestNestTable as table of TestObj;
定義對索引表"造型"后的輸出的游標類型:
create or replace package out_param is?
type out_cur is ref cursor;?????????
下面是嵌套表作為輸出參數的存儲過程:
create or replace procedure testPro2(o_cur out out_param.out_cur ) is
----?包含對象的嵌套表變量的聲明
v_objTable? TestNestTable:= TestNestTable ();
begin
???????????????????? --嵌套表變量的使用
v_objTabl.extend;
v_objTable(1):= TestObj(‘張三',12,123,123,34);
v_objTabl.extend;
v_objTable(2):= TestObj(‘李四,22,223,223,234);
--對嵌套表進行"造型"返回游標
open? o_cur? for??select * from Table(cast (v_objTable?as?TestNestTable) );?
end ?testPro2;
2.3.2? J
ava程序的編寫
????
//從游標返回結果集
public ResultSet getCursor()
{
try
{
......
stmt =(CallableStatement )conn.prepareCall("call testPro2(?)");
//注冊游標對象類型
stmt.registerOutParameter(1,OracleTypes.CURSOR);
stmt.execute();
//返回結果集
ResultSet ?Rs=(ResultSet)stmt.getObject(1);
}
catch(Exception e)
{
}
return Rs;
}
3??結束語
使用索引表和可變數組,可將返回的集合映射成Java數組。由于索引表會自動分配空間,在聲明時不需要指定其大小,而且不需要初始化,使用起來比較方便。但是索引表作為輸出參數只能使用oci驅動(返回游標時,可以用瘦客戶驅動也可以用oci驅動),所需要的動態連接庫文件(ocijdbc9.dll)要在環境變量里進行設置(例如:path=D:\oracle\ora90\BIN),在不同的環境下OCI驅動還可能出現類裝載異常,所以返回索引表盡管方便,但偶爾會出現意想不到的錯誤。可變數組映射成Java數組簡單,對于返回小數據量的結果,也是不錯的選擇,但使用可變數組作為輸出參數,聲明時必須限定該數組的大小上限,并且需使用構造器初始化。
使用嵌套表,可以對嵌套表進行SQL操作,其內容能通過對標準的SELECT?語句造型后可轉化為游標輸出。而且嵌套表的內容相當于session變量,當斷開連接后即釋放內存,但同樣存在需要初始化和擴展的問題。
綜 上所述,究竟采用索引表、嵌套表和可變數組中哪一種作為存儲過程的輸出要看具體的要求和開發環境。有一點我們需要注意,如果返回的數據量較大,以數組形式 返回,則需一次性取回所有結果,在PL/SQL里為所有結果分配空間并復制,然后將這些數據通過網絡發送到客戶端,客戶端也同樣需要分配空間接受這些數 據;而采用游標形式,只要返回一個指針,然后分批返回結果(可自定義每次返回記錄的條數),而不是一次性返回所有結果,因此在客戶端不需分配大塊的空間存 放所有結果。可見,對于大數據量的應用程序,返回游標程序運行效率會更高。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
