JdbcTemplate操做SQLServer存儲過程

最近須要頻繁使用到存儲過程,然而 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"));
相關文章
相關標籤/搜索