最近須要頻繁使用到存儲過程,然而 Jdbc 與 JdbcTemplate 原生的調用實在是有些繁雜,因此我抽空封裝了一個通用的工具類,能拿到結果集與輸出參數。代碼以下:java
package zze.workinglog.utils; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.CallableStatementCallback; import org.springframework.jdbc.core.JdbcTemplate; import java.lang.reflect.Field; import java.sql.*; import java.util.*; public class ProcTemplate { private JdbcTemplate jdbcTemplate; public ProcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } /** * 執行存儲過程 * @param procName 存儲過程名稱 * @param outArgInfo 輸出參數及參數類型 * @param inArgInfoArr 輸入參數及參數值 * @return 結果集封裝爲 List 返回 */ public List exec(String procName, Map<String, Object> outArgInfo, Object... inArgInfoArr) { // 校驗入參個數必須爲偶數 if (!isEven(inArgInfoArr.length)) { throw new RuntimeException("一個入參必須對應一個值"); } if (outArgInfo != null) { // 校驗輸出參數類型必須爲 SQLType Collection<Object> values = outArgInfo.values(); values.forEach(p -> { if (!(p instanceof Integer) || !(isInclude(Integer.parseInt(p.toString())))) { throw new RuntimeException("類型代碼必須在【java.sql.Types】類中已定義"); } }); } // 入參信息整理 Map<String, Object> inArgInfo = new HashMap<>(); String inArgName = ""; for (int i = 0; i < inArgInfoArr.length; i++) { boolean isArgInfo = isEven(i); if (isArgInfo) { // 偶數時爲參數信息 inArgName = inArgInfoArr[i].toString(); } else {// 奇數時爲參數值 inArgInfo.put(inArgName, inArgInfoArr[i]); } } // 拼接執行存儲過程參數佔位符 String procPlaceHolder = genProcPlaceHolder(inArgInfo.size() + (outArgInfo != null ? outArgInfo.size() : 0)); // 要執行的 SQL String execSql = String.format("exec %s %s", procName, procPlaceHolder); return jdbcTemplate.execute(execSql, new CallableStatementCallback<List<Map<String, Object>>>() { @Override public List<Map<String, Object>> doInCallableStatement( CallableStatement cs) throws SQLException, DataAccessException { // 設置入參參數值 for (String inArgName : inArgInfo.keySet()) { cs.setObject(inArgName, inArgInfo.get(inArgName)); } if (outArgInfo != null) { // 註冊輸出參數 for (String outArgName : outArgInfo.keySet()) { cs.registerOutParameter(outArgName, (Integer) outArgInfo.get(outArgName)); } } // 執行存儲過程,得到結果集 ResultSet rs = cs.executeQuery(); List list = convertResultSetToList(rs); if (outArgInfo != null) { // 獲取輸出參數值 for (String outArgName : outArgInfo.keySet()) { outArgInfo.replace(outArgName, cs.getObject(outArgName)); } } return list; } }); } public List convertResultSetToList(ResultSet rs) throws SQLException { // 封裝到 List List<Map<String, Object>> resultList = new ArrayList<>(); ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); while (rs.next()) {// 轉換每行的返回值到Map中 Map rowMap = new HashMap(); for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnName(i); rowMap.put(columnName, rs.getString(columnName)); } resultList.add(rowMap); } rs.close(); return resultList; } /** * 判斷一個數是否是偶數 * * @param num 須要判斷的數字 * @return 若是是返回 true,不然爲 false */ private boolean isEven(int num) { return num % 2 == 0; } /** * 按指定個數生成存儲過程佔位符 * * @param argCount 參數個數 * @return 佔位符字符串,如 ?,?,?,... */ private String genProcPlaceHolder(int argCount) { List<String> placeHolderList = new ArrayList<>(); for (int i = 0; i < argCount; i++) { placeHolderList.add("?"); } return String.join(",", placeHolderList); } /** * 檢查傳入類型代碼是否合法 * * @param key 類型代碼 * @return 若是合法則返回 true,不然返回 false * @throws IllegalAccessException */ private static boolean isInclude(int key) { List<Integer> typeCodeList = new ArrayList<Integer>(); Field[] declaredFields = Types.class.getDeclaredFields(); for (Field declaredField : declaredFields) { try { typeCodeList.add(declaredField.getInt(Types.class)); } catch (IllegalAccessException e) { throw new RuntimeException("類型檢查失敗"); } } return typeCodeList.contains(key); } }
// 定義一個存放輸出參數信息的 Map ,泛型必須爲 Map<String, Object>,若是沒有輸出參數則可直接傳 null Map<String, Object> outArgInfo = new HashMap<>(); // 向輸出參數 Map 中添加輸出參數信息,key 是存儲過程對應輸出參數名稱,值是 java.sql.Types 中的成員變量 outArgInfo.put("remark", Types.VARCHAR); // 執行存儲過程,返回值爲將結果集包裝的 List,輸出參數值直接返回到 outArgInfo // param1:存儲過程名稱 // param2:輸出參數 Map 對象 // param3-n:輸入參數與其值,如 "id",1,"name","zhang",... List list = procTemplate.exec("proc_testOutputParam", outArgInfo, "intUserID", 22340); // 輸出參數值直接從 outArgInfo 中拿到 System.out.println(outArgInfo.get("remark"));