通過和網上的資料結合,先整理了一套通用的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"]}
附上圖:
測試成功!