需求:有一個數據量比較大的表tableA,大概有幾十萬數據。裏面存放用戶手機號碼,如今要求批量保存至少幾百的的手機號碼,保存以前須要分別驗證這些手機號碼是否在數據庫中已存在。 spring
2)、在批量保存以前,把全部的手機號查出來,放到服務器內存;而後在內存中分別對需 要保存的手機號進行校驗,過濾出須要保存號碼,而後在進行批量保存;
缺點:在數據量少的狀況下可使用該方法,可是幾十萬條的數據都要放到內存中 個,一是浪費系統內存空間,致使程序運行緩慢。也不可取。
3)、在保存以前,利用in來查詢庫中存在的手機號碼,而後在把查詢出來手機號分別與須要保存的的手 機號作對比,在查詢出的手機號列表中存在的,則過濾到,而後在批量保存;
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包中便可。
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);