Java Oracle封裝通用的存儲過程適用多數據源

通過和網上的資料結合,先整理了一套通用的Java存儲過程方法

1.建立幾個測試的存儲過程之前要將scott用戶的emp表導入進來,我們的測試代碼都依賴於emp表,創建表的語句

-- Create table
create table EMP
(
  empno    NUMBER(4),
  ename    VARCHAR2(10),
  job      VARCHAR2(9),
  mgr      NUMBER(4),
  hiredate DATE,
  sal      NUMBER(7,2),
  comm     NUMBER(7,2),
  deptno   NUMBER(2)
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );

注意:如果你有scott用戶就直接導入到你的用戶下

 

2.創建測試存儲過程或者包名

--包
create or replace package pkg_cursor is  
  type my_cursor_type is ref cursor;  
end pkg_cursor;  

--過程一
create or replace PROCEDURE "GET_ONE_TEST" (str in  VARCHAR2,v_str out varchar2)
as
v_begintime varchar2(20);
v_endtime varchar2(20);
begin
  v_begintime:=to_char(sysdate,'yyyy-mm-dd hh24:mi:ss');
  select 'badkano'||','||str into v_str from dual;
  v_endtime:=to_char(sysdate,'yyyy-mm-dd hh24:mi:ss');
  dbms_output.put_line('開始時間爲:'||v_begintime);
  dbms_output.put_line('結束時間爲:'||v_endtime);
  dbms_output.put_line('v_str:'||v_str);
end;

--過程二
create or replace PROCEDURE "GET_ONE_TEST_NOPARAM" (v_str out varchar2)
as
v_begintime varchar2(20);
v_endtime varchar2(20);
begin
  v_begintime:=to_char(sysdate,'yyyy-mm-dd hh24:mi:ss');
  select 'badkano' into v_str from dual;
  v_endtime:=to_char(sysdate,'yyyy-mm-dd hh24:mi:ss');
  dbms_output.put_line('開始時間爲:'||v_begintime);
  dbms_output.put_line('結束時間爲:'||v_endtime);
  dbms_output.put_line('v_str:'||v_str);
end;

--過程三
create or replace PROCEDURE "GETBYDEPTNO" (depno in number,job_ in VARCHAR2, emp_cursor out pkg_cursor.my_cursor_type) is  
begin  
  open emp_cursor for  
  select * from emp t where t.deptno = depno and t.job=job_;  
end;

--過程四
create or replace PROCEDURE "GETBYDEPTNO_NOPARAM" (emp_cursor out pkg_cursor.my_cursor_type) is  
begin  
  open emp_cursor for  
  select * from emp t ;  
end;

3.在springboot上面配置你的數據庫連接

spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.username=aaaa
spring.datasource.password=aaaa
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
#打印jdbc日誌
logging.level.org.springframework.jdbc.core.JdbcTemplate: DEBUG

特別說明:如果你配置了MyBatis數據源或者多數據源的話,就省略此步驟,多數據源有一個主次關係,JdbcTemplate默認選擇主數據庫,這個可以和MyBatis兼容使用,由於通用存儲過程用MyBatis比較複雜,我這裏提供的是JdbcTemplate版本的通用封裝,其實MyBatis也是對JDBC的封裝,只是表示形式不同,大家放心使用. 多數據源配置不懂可以在下方留言問我,也可以加我qq 243517277,加好友的時候請註明來源

4.在你的pom.xml中創建SpringJDBC jar依賴

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
			<version>2.2.1.RELEASE</version>
		</dependency>

注意:springboot jar我就不提供了,你們自行查找

5.新建一個controller

package com.cwh.springbootMybatis.controller.produce;

import com.alibaba.fastjson.JSON;
import com.cwh.springbootMybatis.mgrframework.beans.response.IResult;
import com.cwh.springbootMybatis.mgrframework.beans.response.ResultBean;
import oracle.jdbc.OracleTypes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/***
 * 使用SpringJDBC模板來執行通用存儲過程,注意在配置文件配置數據源
 */
@Controller
@RequestMapping("/produce")
public class ProduceController {

    @Autowired
    private JdbcTemplate jdbcTemplate;





    /**帶有入參和回參的存儲過程
     *查詢列表{"name":"用戶.getByDeptno","reqList":[ "30", "SALESMAN"],"resList":[ "empno", "ename", "sal", "deptno" , "job"]}
     * name:存儲過程名字 例如zysoft2019.xxxx
     * reqList:入參 沒有入參就什麼也不傳,否則會報錯
     * resList:回參 沒有回參就什麼也不傳,否則報錯
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/findList", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json;charset=utf-8")
    public IResult findList(@RequestBody String param){
        HashMap hashMap = JSON.parseObject(param, HashMap.class);
        String name=(String) hashMap.get("name");
        List<String> reqList=null;
        if( hashMap.get("reqList") != null){
            reqList=(List<String>) hashMap.get("reqList");
        }
        List<String> resList=(List<String>) hashMap.get("resList");

        List<Map> list=callProcedure(name,reqList,resList);
        return new ResultBean<Collection<Map>>(list);
    }

    /**
     * 只有一個輸出參數
     * @param param reqList:入參 沒有入參就什麼也不傳,否則會報錯
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/findOnlyOne", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json;charset=utf-8")
    public IResult findOnlyOne(@RequestBody String param){
        HashMap hashMap = JSON.parseObject(param, HashMap.class);
        String name=(String) hashMap.get("name");
        List<String> reqList=null;
        if( hashMap.get("reqList") != null){
            reqList=(List<String>) hashMap.get("reqList");
        }
        String resStr=(String) hashMap.get("res");
        String res= callProcedureOnlyoneOut(name,reqList,resStr);
        return new ResultBean<String>(res);
    }






/************************************************************************私有方法區**************************************************************************************/


    /***
     * *  獲取數據庫內的存儲過程--返回一個List
     * @param procedureName 存儲過程名
     * @param inParameter   輸入的參數
     * @param outParamter   輸出的參數
     * @return
     */
    private List<Map> callProcedure(final String procedureName,final List inParameter,final List outParamter){
        if(procedureName==null || procedureName.length() == 0 ){
            return null;
        }
        //沒有返回參數
        if(outParamter == null ){
            if(callProcedureWithoutOut(procedureName,inParameter))return null;
        }
        List resultList = (List) jdbcTemplate.execute(
                new CallableStatementCreator() {
                    public CallableStatement createCallableStatement(Connection con) throws SQLException {
                        int inSize = inParameter==null?0:inParameter.size();
                        //int outSize = outParamter==null?0:outParamter.size();
                        StringBuffer sbsql = new StringBuffer();
                        sbsql.append("{call "+procedureName).append("(");
                        //注意這裏面返回的是遊標,加1即可
                        for(int i=0;i<(inSize+1);i++){
                            if(i == 0){
                                sbsql.append("?");
                            }else{
                                sbsql.append(",?");
                            }
                        }
                        sbsql.append(")}");
                        CallableStatement cs = con.prepareCall(sbsql.toString());
                        // 設置輸入參數的值
                        if(inSize > 0 ){
                            String typeName =  null;
                            for(int i=0;i<inSize;i++){
                                typeName = inParameter.get(i).getClass().getName().toString();
                                cs.setObject(i+1, inParameter.get(i));
                            }
                        }
                        // 註冊輸出參數的類型
                        cs.registerOutParameter(inSize+1, OracleTypes.CURSOR);
                        return cs;
                    }
                }, new CallableStatementCallback<List>() {
                    public List<Map> doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
                        int inSize = inParameter==null?0:inParameter.size();
                        List<Map> resultsMap = new ArrayList();
                        cs.execute();
                        ResultSet rs = (ResultSet) cs.getObject(inSize+1);// 獲取遊標一行的值
                        while (rs.next()) {// 轉換每行的返回值到Map中
                            Map rowMap = new HashMap();
                            for(int i=0;i<outParamter.size();i++){
                                String outP = outParamter.get(i).toString();
                                rowMap.put(outP.toLowerCase(),rs.getObject(outP.toUpperCase()) );
                            }
                            resultsMap.add(rowMap);
                        }
                        rs.close();
                        return resultsMap;
                    }

                });
        return resultList;

    }

    /**
     *
      獲取數據庫內的存儲過程--沒有輸出
     * @param procedureName 存儲過程名
     * @param inParameter 輸出的參數
     * @return
     */
    private Boolean callProcedureWithoutOut(final String procedureName,final List inParameter){
        if(procedureName==null || procedureName.length() == 0 ){
            return false;
        }
        StringBuffer sbsql = new StringBuffer();
        sbsql.append("{call "+procedureName).append("(");
        for(int i=0;i<inParameter.size();i++){
            if(i == 0){
                sbsql.append(inParameter.get(i));
            }else{
                sbsql.append(","+inParameter.get(i));
            }
        }
        sbsql.append(")}");
        jdbcTemplate.execute(sbsql.toString());
        return true;
    }


    /*
     *  獲取數據庫內的存儲過程--輸出只有一個參數
     * @param procedureName
     * @param inParameter
     * @param outParamer
     * @return
     */
    private String callProcedureOnlyoneOut(final String procedureName,final List inParameter,final String outParamer){
        if(procedureName==null || procedureName.length() == 0 ){
            return null;
        }
        String result = (String) jdbcTemplate.execute(
                new CallableStatementCreator() {
                    public CallableStatement createCallableStatement(Connection con) throws SQLException {
                        int inSize = inParameter==null?0:inParameter.size();
                        StringBuffer sbsql = new StringBuffer();
                        sbsql.append("{call "+procedureName).append("(");
                        for(int i=0;i<(inSize+1);i++){
                            if(i == 0){
                                sbsql.append("?");
                            }else{
                                sbsql.append(",?");
                            }
                        }
                        sbsql.append(")}");
                        System.out.println("SQL語句:"+sbsql);
                        CallableStatement cs = con.prepareCall(sbsql.toString());
                        // 設置輸入參數的值
                        if(inSize > 0 ){
                            String typeName =  null;
                            for(int i=0;i<inSize;i++){
                                typeName = inParameter.get(i).getClass().getName().toString();
                                cs.setObject(i+1, inParameter.get(i));
                            }
                        }
                        // 註冊輸出參數的類型
                        cs.registerOutParameter(inSize+1, OracleTypes.VARCHAR);
                        return cs;
                    }
                }, new CallableStatementCallback<Object>() {
                    public Object doInCallableStatement(CallableStatement cs) throws SQLException,DataAccessException {
                        int inSize = inParameter==null?0:inParameter.size();
                        List<Map> resultsMap = new ArrayList();
                        cs.execute();
                        return  cs.getObject(inSize+1);// 獲取遊標一行的值
                    }

                });
        return result;
    }




}

說明:

1.我寫的controller需要用到其它的依賴類,如果你們在導入這個controller文件報錯的話,請按需修改,裏面用到了com.alibaba.fastjson這個jar,請大家自行去下載.

 2.有兩個通用方法findList和findOnlyOne;前者是返回的遊標,後者是返回的字符數據類型,如果你沒有設置好類型和參數個數,就會報類型和個數不匹配的錯誤.

3.如果你的存儲過程不在當前用戶下,調用存儲過程的時候要傳入用戶名,例如aaaa.存儲過程名稱,否則會報權限不夠的錯誤.

4.postman的測試樣例採用json傳參數,controller層是將String類型轉爲Map的,通過map來取參數,因此前端post傳參數必須是json格式,例如:

{"name":"aaaa.getByDeptno","reqList":[ "30", "SALESMAN"],"resList":[ "empno", "ename", "sal", "deptno" , "job"]}

附上圖:

測試成功!

發佈了33 篇原創文章 · 獲贊 7 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章