JAVA 执行存储过程(和参数顺序无关,适用oracle数据库)

通常jdbc调用oracle存储过程的输入输出参数都是按顺序,和java端的参数名称没关系,这样当存储过程修改参数时不是非常方便,字段一旦多的话容易错位,这类问题的排查效率也很低,所以想看看有没有办法java端的key值能和sql的参数名称匹配起来。后来发现pl/sql可以用“参数名=>参数值”的方式调用存储过程。java端的DatabaseMetaData 类也有getProcedureColumns可以得到所有的输入输出参数,这样我的想法就可以通过这两点得以实现。以下就是输入和放回都是map的调用存储过程的方法:

/**
* 执行存储过程(和参数顺序无关)
* @param procName 存储过程名称
* @param procParam 参数(key需大写)
* @return
*/
public static Map<String, Object> executeProcedure(String procName,
Map<String, Object> procParam) {
Connection conn = null;
CallableStatement cs = null;
PreparedStatement ps = null;
ResultSet rs=null;
String sqlCall = "";
Map<String, List<Integer>> outParaIdxMap = new LinkedHashMap<String, List<Integer>>();
try {
conn = getConnection(dbResourceName);
DatabaseMetaData dbmd = conn.getMetaData();
sqlCall = "{call " + procName + "(";
String pckgName = procName.lastIndexOf(".") > 0 ? procName
.substring(0, procName.lastIndexOf(".")).toUpperCase()
: null;//有包体时
String proc = procName.substring(procName.lastIndexOf(".") + 1,
procName.length()).toUpperCase();
rs = dbmd.getProcedureColumns(pckgName,"" , proc, null);
int i = 1;

while (rs.next()) {// 拼装参数
String columnName = rs.getString("COLUMN_NAME");
Short columnType = rs.getShort("COLUMN_TYPE");
int dataType = rs.getInt("DATA_TYPE");
List<Integer> outParaList=new ArrayList<Integer>();
if (DatabaseMetaData.procedureColumnIn==columnType){
sqlCall += columnName + "=>'" + procParam.get(columnName)
+ "',";
}
else if (DatabaseMetaData.procedureColumnOut==columnType) {// 输出参数
sqlCall += columnName + "=>?,";
outParaList.add(i);
outParaList.add(dataType);
outParaIdxMap.put(columnName,outParaList);

i++;
}

}
sqlCall = sqlCall.substring(0, sqlCall.lastIndexOf(",")) + ")}";//去除参数最后一个逗号以及后面的部分
cs = conn.prepareCall(sqlCall);
for (String key : outParaIdxMap.keySet()) {
int idx=outParaIdxMap.get(key).get(0);//顺序
int dataType=outParaIdxMap.get(key).get(1);//dataType
cs.registerOutParameter(idx, dataType);
}
cs.execute();
for (String key : outParaIdxMap.keySet()) {//组装输出参数值(保留输入参数值)
int idx=outParaIdxMap.get(key).get(0);//顺序
procParam.put(key, cs.getObject(idx));
}
conn.commit();


} catch (Exception e) {
System.err.println("SQLException proc=[" + sqlCall + "] ");
try {
if (conn != null)
conn.rollback();
} catch (SQLException sqlex) {
sqlex.printStackTrace();
}
e.printStackTrace();

} finally {
closeRsPsConn(rs, ps, conn);
closeCsPsConn(cs, ps, conn);
}
return procParam;
}
/**
* 把key装换为大写便于后台处理
* @param map
* @return
*/
public static Map<String, Object> convertUpperCaseMap(Map<String, ?> map) {
Map<String, Object> returnMap=new LinkedHashMap<String, Object>();
for (String key : map.keySet()) {
returnMap.put(key.toUpperCase(), map.get(key));
}
return returnMap;
}

开发中遇到的问题:
1.本来CallableStatement 是提供setString(ParameterName,value)这样的方法的,但是看了ibm网站上的资料发现不支持oracle,是infomix的特性,所以无法用,只能麻烦点用index的方式注册输出参数
2.另外所有的参数包括存储过程名称、包名都要大写,否则不认得
3.使用getProcedureColumns(pckgName,"" , proc, null);时注意 第一个参数catalog对应oracle的包名,第二个参数应该是schema名称,我一开始以为是用户名,但不对,只能写空,但不要写null,否则会得到这个实例里面所有这个存储过程的参数,当实例存在多个用户的使用会有问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值