j2ee調用Oracle帶數組參數和遊標的存儲過程方法

環境:Oracle 10g; jboss4.2.2;jdk1.6;hibernate 3.2 java

需求:有一個數據量比較大的表tableA,大概有幾十萬數據。裏面存放用戶手機號碼,如今要求批量保存至少幾百的的手機號碼,保存以前須要分別驗證這些手機號碼是否在數據庫中已存在。 spring


思路:1)、在每一個號碼保存以前,查詢數據庫,是否是已經有了。
           缺點:每次保存以前都要查詢數據庫,與數據庫交互過於頻繁,效率很是差;

      2)、在批量保存以前,把全部的手機號查出來,放到服務器內存;而後在內存中分別對需                         要保存的手機號進行校驗,過濾出須要保存號碼,而後在進行批量保存;
            優勢:減小了與數據庫交互的次數;節省了系統資源
            缺點:在數據量少的狀況下可使用該方法,可是幾十萬條的數據都要放到內存中                                 個,一是浪費系統內存空間,致使程序運行緩慢。也不可取。

      3)、在保存以前,利用in來查詢庫中存在的手機號碼,而後在把查詢出來手機號分別與須要保存的的手            機號作對比,在查詢出的手機號列表中存在的,則過濾到,而後在批量保存;
           優勢:相比一、2點來講這個方法比較好一些,適合保存數據很少的狀況下;
           肯定:若是一次性須要保存號碼不少的好比幾百條,幾千條的時候,效率依然不好。

      4)、利用臨時表:在數據庫中建立一個臨時表(事務級);而後在創建一個存儲過程;在進行數據保存            時將須要保存的手機號碼一次性傳入存儲過程當中,在存儲過程當中先將須要保存的號碼插入到臨時表            中,而後在利用臨時表關聯實際表,查出表中已存在的號碼,而後返回給程序,在程序中根據返回            的手機號碼列表進行重複性過濾;
           優勢:速度快,能夠應對大數據量的數據操做。節省資源。
           缺點:若是業務發生變化,須要同時維護程序和存儲過程代碼。程序在進行存儲過程調用時,不方                  便調試。

綜上所述:對於大數據量而言,利用第四點比較可取。現列出操做方法:

1、創建存儲過程:(數據庫中操做)
    一、聲明type類型,即創建數據數組類型:
        create or replace type mobileArray as table of varchar2(30);
   (補充:進行數據類型建立時有兩種方式:
           一、固定長度數組:建立時須要制定長度:create or replace  類型名 AS VARRAY(52) OF VARCHAR2(20);
           二、嵌套表:可變長度數組:create or replace type  類型名 as table of varchar2(30);
 本實例使用嵌套表。)

   二、建立臨時表:create global temporary table TEMP_SEARCH_SUBSCRIBE_MOBILE(mobile VARCHAR2(30))
                  on commit delete rows; 
      (補充: 臨時表分爲SESSION、TRANSACTION兩種,SESSION級的臨時表數據在整個SESSION都存在,直到結束這次SESSION;而TRANSACTION級的臨時表數據在TRANACTION結束後消失,即COMMIT/ROLLBACK或結束SESSION都會清除TRANACTION臨時表數據。  
兩種臨時表的語法: 
    create global temporary table 臨時表名 on commit preserve|delete rows  用preserve時就是SESSION級的臨時表,用delete就是TRANSACTION級的臨時表;  
   三、創建存儲過程:
      create or replace procedure proc_query_subsceibe_mobiles(mobileList in mobileArray,cur_mobileList out sys_refcursor)
 as
   --c number;
begin
  for i in 1..mobileList.count loop
      insert into TEMP_SEARCH_SUBSCRIBE_MOBILE(MOBILE) values(mobileList(i));
  end loop;
  open cur_mobileList for 
     select tempSub.MOBILE from TEMP_SEARCH_SUBSCRIBE_MOBILE tempSub
     where exists (Select 1 from mobile_subscribe_info msi 
     where msi.user_mobile = tempSub.Mobile 
     and msi.OPERATION_TYPE='1' and (msi.OPRCODE <> '04' or msi.SUBSCRIBE_STATUS <> 2));
     --select count(*) into c from TEMP_SEARCH_SUBSCRIBE_MOBILE;
     -- dbms_output.put_line('查詢到的數據個數: '||c);
     -- commit;
end proc_query_subsceibe_mobiles;

  四、創建測試代碼:測試存儲過程的重要性
     declare 
  cur_calling sys_refcursor;

  my_tbl mobileArray := mobileArray('13937025312', '13937025313', '13937025314');
  
  rec_next varchar2(30);
begin
   proc_query_subsceibe_mobiles(my_tbl,cur_calling);  --這樣這個遊標就有值了
    loop
     fetch cur_calling into rec_next;
     exit when cur_calling%notfound;
     dbms_output.put_line(rec_next);
    end loop;
end;

2、程序中調用存儲過程:
    一、spring jdbcTemplate調用存儲過程實例:
       public  List<String> getSubscribeMobileList(Map<String,Object> map) {
final List<String>  mobileList = (List<String>) map.get("mobiles");
final JdbcTemplate jdbcTemplateMehtod = this.jdbcTemplate;
   
List resultList = (List) jdbcTemplate.execute(new CallableStatementCreator() { 
       public CallableStatement createCallableStatement(Connection conn) throws SQLException { 
          String procSql="{Call proc_query_subsceibe_mobiles(?,?)}";// 調用的sql 
          CallableStatement proc  = conn.prepareCall(procSql);
          Connection oracleConn = conn.getMetaData().getConnection();
       oracle.sql.ArrayDescriptor mobileArrayDes =
               oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY", oracleConn);
       oracle.sql.ARRAY mobileArray = new oracle.sql.ARRAY(mobileArrayDes,  oracleConn, mobileList.toArray());
       String[] array = (String[]) mobileArray.getArray();
       proc.setArray(1, mobileArray);
       proc.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);//輸出參數
          return proc; 
       } 
    }, new CallableStatementCallback() { 
       public Object doInCallableStatement(CallableStatement cs) throws SQLException,DataAccessException { 
          List<String> mobilesList = new ArrayList<String>(); 
          cs.execute(); 
          ResultSet rs = (ResultSet) cs.getObject(2);// 獲取遊標一行的值 
          while (rs.next()) {// 轉換每行的返回值到Map中 
          String mobile = rs.getString("MOBILE");
          if(!StringUtils.isEmpty(mobile)&&!mobilesList.contains(mobile)){
          mobilesList.add(mobile);
          }
         
          } 
          rs.close(); 
          return mobilesList; 
       } 
 });  
 return resultList;

二、純jdbc調用實例:
   public  List<String> getSubscribeMobileList2(Map<String,Object> map) {
List<String> mobilesList = new ArrayList<String>();
List<String> mobileList = (List<String>) map.get("mobiles");
ResultSet rs=null;
String driver = "oracle.jdbc.driver.OracleDriver";
String strUrl = "jdbc:oracle:thin: @127.0.0.1 :1521:orcl";
Connection conn = null;  
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
conn = DriverManager.getConnection(strUrl, "xy_mms", "xy_mms");
conn.setAutoCommit(false);
String procSql="{Call proc_query_subsceibe_mobiles(?,?)}";    
       CallableStatement proc  = conn.prepareCall(procSql); 
       oracle.sql.ArrayDescriptor mobileArrayDes =
                oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());
       oracle.sql.ARRAY mobileArray = new oracle.sql.ARRAY(mobileArrayDes, conn.getMetaData().getConnection(), mobileList.toArray());
       proc.setArray(1, mobileArray);
       proc.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);//輸出參數
       proc.execute();
       rs = (ResultSet) proc.getObject(2); 
       while(rs.next()){
        mobilesList.add(rs.getString("MOBILE"));
       }
       
       conn.commit();
       proc.close();
       conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return mobilesList;
}

三、hibernate 調用存儲過程:
   public  List<String> getSubscribeMobileList2(Map<String,Object> map) {
List<String> mobilesList = new ArrayList<String>();
List<String> mobileList = (List<String>) map.get("mobiles");
ResultSet rs=null;
Session session = this.getSession();
Connection conn = session.connection();  
try {

conn.setAutoCommit(false);
String procSql="{Call proc_query_subsceibe_mobiles(?,?)}";    
       CallableStatement proc  = conn.prepareCall(procSql); 
       oracle.sql.ArrayDescriptor mobileArrayDes =
                oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());
       oracle.sql.ARRAY mobileArray = new oracle.sql.ARRAY(mobileArrayDes, conn.getMetaData().getConnection(), mobileList.toArray());
       proc.setArray(1, mobileArray);
       proc.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);//輸出參數
       proc.execute();
       rs = (ResultSet) proc.getObject(2); 
       while(rs.next()){
        mobilesList.add(rs.getString("MOBILE"));
       }
       
       conn.commit();
       proc.close();
       conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return mobilesList;
}

須要注意:
1.關於字符集:11g的jdbc驅動叫orai18n.jar,以前是nls_charset.jar/classes12.jar 
2.ArrayDescriptor:java傳入oracle的數組須要處理一下 
3.oracle.jdbc.OracleTypes.CURSOR:java得到oracle的遊標。 

四、
oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());該劇中的MOBILEARRAY必定要爲大寫,不然會報錯誤;該值就是在第一步建立的的oracle type數組。

五、在運行程序時,可能會發現沒有查詢到數據,這是任務,在進行建立Oracle array數組是,傳遞過來的mobiles數組沒有正確狀態,此時須要查看系統中是否引入了nls_charset12.jar(Oracle 11g之前使用該包,之後的須要使用 orai18n.jar;兩個均可以在Oracle安裝目錄中搜索獲得),缺乏該jar包,則沒法正常裝載字符串數組值。將其進入至庫中便可。

六、以上都準備好之後,分別運行時發現,只有第二種利用jdbc的能夠正常運行,其餘兩種會報,OracleConnect類轉化異常。
因爲本例使用的是jboss,在該例中解決方案是把jboss服務器中deploy中的項目發佈包中的ojdbc14.jar和nls_charset12.jar都刪掉,將這個兩個jar包移動到jboss 的lib包中便可。

PS:在使用hibernate時,因爲session.connect方法已被丟棄,因此,能夠考慮換一種方法使用doWork方法:
Session session = this.getSession();
Work work = new Work(){
@Override
public void execute(Connection conn) throws SQLException {
ResultSet rs=null;
try {
conn.setAutoCommit(false);//此處須要禁止鏈接的自動提交,不然會取不到值
String procSql="{Call proc_query_subsceibe_mobiles(?,?,?,?)}";    
       CallableStatement proc  = conn.prepareCall(procSql); 
       oracle.sql.ArrayDescriptor mobileArrayDes =
               oracle.sql.ArrayDescriptor.createDescriptor("MOBILEARRAY",conn.getMetaData().getConnection());
       oracle.sql.ARRAY mobileOralceArray = new oracle.sql.ARRAY(mobileArrayDes, conn.getMetaData().getConnection(), needSaveMobileArray.toArray());
       proc.setArray(1, mobileOralceArray);
       proc.setString(2, finalOperationType);
       proc.setString(3, queryFlag);
       proc.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);//輸出參數
       proc.execute();
       rs = (ResultSet) proc.getObject(4); 
       while(rs.next()){
        mobilesList.add(rs.getString("MOBILE"));
       }
       conn.commit();//提交事務,清除臨時表數據
       proc.close();
       conn.setAutoCommit(true);//此處須要打開自動提交功能,不然,下面的事務不會提交到數據庫中
} catch (SQLException e) {
e.printStackTrace();
}
}
};
session.doWork(work);
相關文章
相關標籤/搜索