使用java呼叫儲存過程的通用封裝
阿新 • • 發佈:2019-01-22
對於在java中呼叫儲存過程,我一直因為是一件比較頭疼的事情,因為各個資料庫的實現往往不相同,這樣就很難保證同一套程式在sqlserver和oracle上都能執行.儘管兩個資料庫中的儲存過程名字和引數已經基本相同,以前用spring封裝過一個,用了幾個抽象類做了簡單的設計,但是總感覺不是很好,而且最新的spring貌似已經廢棄那幾個類了,看來是有問題,週末花了些時間重新寫了一個比較簡單赤裸的封裝.程式碼雖然不多但是就不一一講解了.這裡先把儲存過程發出來.
sqlsever版
@outputParamint output,
@outputParamint output,
@idintasselect@outputParam=@idreturn@outputParam/*返回一個結果集*/createprocedure p3
@outputParamint output,
@idintasselect@outputParam=@idselect@outputParam/*最簡單的形式*/createprocedure p4
as
對應的oracle版,oracle中儲存過程好像不能直接返回值.至少我還不知道怎麼返回.目前依靠out引數返回遊標作為結果集。
outputParam OUT int,
id INint,
CURSOR1 out sys_refcursor,
CURSOR2 out sys_refcursor
) ASBEGINselect id into p1.outputParam from dual;
OPEN CURSOR1 FORselect'syj1
OPEN CURSOR2 FORselect'abc1'as id from dual unionallselect'abc2'as id from dual;
END p1;
CREATEORREPLACEPROCEDURE p3(
outputParam IN OUT int,
id INint,
CURSOR1 out sys_refcursor
) ASBEGINselect id into P3.outputParam from dual;
OPEN CURSOR1 FORselect'syj1'as id from dual unionallselect'syj2'as id from dual;
END P3;
CREATEORREPLACEPROCEDURE p4
ASBEGIN
dbms_output.put_line('helloword');
END P4;
因為mssql和oracle對結果集的返回形式不相同所以就很難在java程式碼中保證一致的呼叫方式。解決這個方法的唯一途徑就是分析他們的相同和不同之處,想辦法在設計上解決。經典的jdbc呼叫,在這裡就不說了,百度一下有很多資料,下面說下經過我這次封裝後的呼叫方式,無論是oracle還是mssql都可以使用下面程式碼執行。只要把ORACLE過程中的遊標引數放置到最後宣告就可以了。
publicvoid test1() {ProcedureResult result = getSimpleJdbcTemplate().execProcedure("p1",
new ProcedureCallBack() {//執行返回結果集的過程public Object mapRow(ResultSet rs, int rowNum)
throws SQLException {
return rs.getObject(1);
}
publicvoid registerParameter() throws SQLException {
registerOutParameter(1, Types.INTEGER);
setInt(2, 9);
addOracleCursor(3);
addOracleCursor(4);
}
});
TestUtil.println(result);
}
publicvoid test2() {//執行直接返回值的過程 ProcedureResult result = getSimpleJdbcTemplate().execProcedure("p2",
Types.INTEGER, new ProcedureCallBack() {
publicvoid registerParameter() throws SQLException {
registerOutParameter(1, Types.INTEGER);
setInt(2, 9);
addOracleCursor(3);
}
});
TestUtil.println(result);
}
publicvoid test4() {//執行普通的簡單過程 ProcedureResult result = getSimpleJdbcTemplate().execProcedure("p4",
new ProcedureCallBack() {
});
TestUtil.println(result);
}
更多使用方法可以參考junit測試程式碼包
核心程式碼部分摘要如下關鍵地方我都加了註釋
/*** 執行儲存過程
*
* @param procedure過程名
* @param sqlType
* 返回值型別,無返回值傳null
* @param procedureCallBack
* 回撥物件
* @return*/private ProcedureResult execProc(String procedure, Integer sqlType,
ProcedureCallBack procedureCallBack) {
Connection conn =null;
Statement stmt =null;
ResultSet rs =null;
ProcedureResult procResult =new ProcedureResult();
try {
procedureCallBack.jt =this;
conn = getConnection();// 取連線 procedureCallBack.registerParameter();// 計算引數的個數int parameterSize = procedureCallBack.parametersCount;// 取引數的個數 procedureCallBack.parametersCount =0;
if (parameterSize >0)
procedure = procedure + getProcParameters(parameterSize, "?");// 根據引數的個數拼裝sql String sql ="{"+ (sqlType !=null?"?=" : "") +"call "+ procedure +"}";
logger.debug("exec "+ sql);
CallableStatement cstm = conn.prepareCall(sql);// 預編譯過程 procedureCallBack.cstm = cstm;
procedureCallBack.startParametersIdx = sqlType !=null?1 : 0;// 對外界忽略帶返回值過程的引數下標if (sqlType !=null)
procedureCallBack.registerOutParameter(0, sqlType.intValue());// 註冊帶返回值過程的返回型別oracle不支援mssql支援 procedureCallBack.registerParameter();// 註冊引數boolean results = cstm.execute();// 執行過程int rsIndex =1;// 結果集個數計數器for (; results; rsIndex++) {
rs = cstm.getResultSet();// 取當前結果集 List list =new ArrayList();// 為當前結果集建立一個容器while (rs.next())
list.add(procedureCallBack.mapRow(rs, rsIndex));
procResult.addRs(list);
results = cstm.getMoreResults();// 取下一個結果集 }
Map map = procedureCallBack.getOutParameters();// 取宣告的out引數, Iterator iterator = map.keySet().iterator();// 遍歷宣告的out引數while (iterator.hasNext()) {
String key = iterator.next().toString();// out引數型別int idx = Integer.valueOf(key).intValue();
if (new Integer(-10).equals(map.get(key))) {// oracle結果集列表是通過遊標的形式利用out引數輸出的oracle.jdbc.OracleTypes.CURSOR的值是-10 closeResultSet(rs);// 關閉上次使用的結果集 rs = (ResultSet) cstm.getObject(Integer.valueOf(key)
.intValue());// 從out引數中取結果集 List list =new ArrayList();// 為當前結果集建立一個容器while (rs.next())
list.add(procedureCallBack.mapRow(rs, rsIndex));
rsIndex++;// 結果集計數器累加 procResult.addRs(list);
} elseif (idx ==1&& sqlType !=null)// 如果過程有返回值,從第一個out引數中取得這個返回值 procResult.setValue(JdbcUtils.getCallableStatementValue(
cstm, idx));
else
procResult.getOutput().add(
JdbcUtils.getCallableStatementValue(cstm, idx));// 取的普通的out引數輸出到out輸出容器 }
if (sqlType ==null&& procResult.getOutput().size() >0)
// 如果過程沒用返回值,例如oracle過程根本不支援返回值,將第一個非遊標的out引數返回值當作過程返回值 procResult.setValue(procResult.getOutput().get(0));
} catch (SQLException e) {
thrownew DataAccessException(e);
} finally {
closeResultSet(rs);
closeStatement(stmt);
closeConnection(conn);
}
return procResult;
}
publicclass ProcedureResult {
private List rs =new ArrayList();//過程返回的n個結果集列表private List output =new ArrayList();//過程的out引數輸出private Object value;//過程本身的返回值 略
}
上面程式碼基本上保證了在oracle和sqlserver兩個資料庫中儲存過程名稱相同引數順序相同(oracle中的遊標置於最後)的的情況下,使用同樣的java程式碼進行呼叫。不知道是否有遺漏之處,如果有那個地方還沒有覆蓋到或者有bug請來訪朋友指正,共同學習,分析快樂。