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

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