一、概述
如果想要執行存儲過程,我們應該使用 CallableStatement 接口。
CallableStatement 接口繼承自PreparedStatement 接口。所以CallableStatement 接口包含有Statement 接口和PreparedStatement 接口定義的全部方法,但是並不是所有的方法我們都要使用,主要使用的方法有這樣幾個:
CallableStatement 常用方法:
返回類型 | 方法簽名 | 說明 |
boolean | execute() | 執行 SQL 語句,如果第一個結果是 ResultSet 對 |
void | registerOutParameter(int parameterIndex,int sqlType) |
按順序位置parameterIndex 將OUT 參數注冊為 |
Type | getType(int parameterIndex) |
根據參數的序號獲取指定的 JDBC 參數的值。第一 |
我們可以使用execute()方法來執行存儲過程。CallableStatement 為所有的數據庫提供了一種統一的標准形式調用存儲過程。所以,你將會看到我們使用execute()調用存儲過程的語法與在Oracle 中會所有不同。
為了獲得存儲過程或函數的返回值,我們需要使用 registerOutParameter()方法將返回的參數注冊為JDBC 的類型。 registerOutParameter()方法的第一個參數是參數的序號,第一個為1,第二個為2,以此類推。第二個參數需要一個int 值,用來標記JDBC 的類型,我們可以使用java.sql.Types 類中的常量來設置這個參數。比如VARCHAR、DOUBLE 等類型。如果類型不夠用,也可以從具體數據庫的驅動中尋找合適的類型常量。如果存儲過程或函數有返回值,這個方法是必須要調用的,否則無法得到返回值,甚至會發生異常。
CallableStatement 接口中定義了很多get 方法,用於獲取存儲過程返回的值,根據值的類型不同,你可以使用不同get 方法,比如getInt()、getString()、getDouble()等等。
我們看一下使用CallableStatement 接口執行存儲過程和函數的語法格式。
存儲過程:{call <procedure-name>[(<arg1>,<arg2>, ...)]}
函數:{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
如果要調用存儲過程,則使用第一種語法,就是開頭不帶問號的語法,call 后面是過程名,
如果沒有參數,可以省略小括號。
如果要調用函數,則使用第二種語法,開頭帶有一個問號加等號,實際上這個問號就是一個占位符,這個問號總是調用函數的第一個占位符。其它部分與過程的語法相同
二、CallableStatement 執行存儲過程
2.1、建立基類
package com.pb.emp.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.pb.emp.untily.ConfigManager; public class BaseDao { protected Connection conn; protected PreparedStatement ps; protected ResultSet rs; //建立連接 public boolean getConnection(){ String driver=ConfigManager.getInstance().getString("jdbc.driver_class"); String url=ConfigManager.getInstance().getString("jdbc.connection.url"); String username=ConfigManager.getInstance().getString("jdbc.connection.username"); String password=ConfigManager.getInstance().getString("jdbc.connection.password"); try { Class.forName(driver); conn=DriverManager.getConnection(url,username, password); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } return true; } //增加,修改,刪除 public int executeUpdate(String sql, Object[] params){ getConnection(); int updateRow=0; try { ps=conn.prepareStatement(sql); //填充占位符 for(int i=0;i<params.length;i++){ ps.setObject(i+1, params[i]); } updateRow = ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return updateRow; } // //查詢 public ResultSet executeSQL(String sql, Object[] params){ getConnection(); try { ps=conn.prepareStatement(sql); //填充占位符 for(int i=0;i<params.length;i++){ ps.setObject(i+1, params[i]); } rs = ps.executeQuery(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return rs; } // 關閉資源 public boolean closeResource() { if(rs!=null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } if(ps!=null){ try { ps.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } return true; } }
2.2、執行不帶參但是有返回值的存儲過程
新建類來繼承上面的類也可以繼承,下面建立存儲過程
--查詢emp表記錄數 CREATE OR REPLACE PROCEDURE getEmpCount(v_count OUT NUMBER) AS BEGIN SELECT COUNT(*) INTO v_count FROM emp; END;
調用
//執行不帶參但是有返回值的存儲過程獲取emp表總記錄數 public int getTotalCountProc(){ //定義一個變量來接收結果 int totalCount=0; //聲明CallableStatement對象 CallableStatement proc=null; String sql="{call getEmpCount(?)}"; try { //建立連接 getConnection(); //CallableStatement對象 proc=conn.prepareCall(sql); //將數據庫對象數據類型注冊為java中的類型 proc.registerOutParameter(1, Types.INTEGER); //執行 proc.execute(); //接收返回值 totalCount=proc.getInt(1); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return totalCount; }
2.3、執行帶參帶返回值的存儲過程
--根據部門編號和姓名查詢人數 CREATE OR REPLACE PROCEDURE getEmpCount(v_deptno NUMBER, v_ename VARCHAR2,v_count OUT NUMBER) AS BEGIN SELECT COUNT(*) INTO v_count FROM emp WHERE ename LIKE '%'||v_ename||'%' AND deptno=v_deptno; END;
//執行帶參帶返回值的存儲過程 public int getTotalCountProc1(int deptno,String ename){ //定義一個變量來接收結果 int totalCount=0; //聲明CallableStatement對象 CallableStatement proc=null; String sql="{call getEmpCount(?,?,?)}"; //建立連接 getConnection(); //CallableStatement對象 try { proc=conn.prepareCall(sql); //設置占位符 //Object [] params={deptno,ename}; //只設置輸入參數即可 proc.setInt(1, deptno); proc.setString(2, ename); //proc.setInt(3, totalCount); ////將數據庫對象數據類型注冊為java中的類型,將輸出參數轉換 proc.registerOutParameter(3, Types.INTEGER); //執行 proc.execute(); //獲取結果 totalCount=proc.getInt(3); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ this.closeResource(); if(proc!=null){ try { proc.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return totalCount; }
2.3、執行返回值為游標的存儲過程
--查詢員工所有信息 CREATE OR REPLACE PROCEDURE emp_cur(emp_cur OUT SYS_REFCURSOR) AS BEGIN OPEN emp_cur FOR SELECT * FROM emp; END;
//執行返回值為游標的存儲過程 游標名emp_cur public List<Emp> getempProc1(){ List<Emp> emplist=new ArrayList<Emp>(); String sql="{call emp_cur(?) }"; //聲明CallableStatement對象 CallableStatement proc=null; //建立連接 getConnection(); try { //執行 proc=conn.prepareCall(sql); //注冊類型為數據庫游標類型 proc.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR); //接收結果集 proc.execute(); //獲取結果第一個對象 rs=(ResultSet) proc.getObject(1); while(rs.next()){ int empno=rs.getInt("empno"); String ename=rs.getString("ename"); String job=rs.getString("job"); int mgr=rs.getInt("mgr"); Date hiredate=rs.getDate("hiredate"); double sal=rs.getDouble("sal"); double comm=rs.getDouble("comm"); int deptno=rs.getInt("deptno"); //聲明Emp對象 Emp emp=new Emp(); //將得到的值添加到對象中 emp.setEmpno(empno); emp.setEname(ename); emp.setJob(job); emp.setMgr(mgr); emp.setHiredate(hiredate); emp.setSal(sal); emp.setComm(comm); emp.setDeptno(deptno); //將對象添加到集合 emplist.add(emp); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ this.closeResource(); if(proc!=null){ try { proc.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return emplist; }
以上看出,需要將輸出的參數,和結果注冊,輸入的參數不要注冊,
但輸入參數需要設置占位符
三、執行函數
3.1 、函數功能為根據雇員id 返回姓名
CREATE OR REPLACE FUNCTION getename(v_empno NUMBER) RETURN VARCHAR2 AS v_ename VARCHAR2(20); BEGIN SELECT ename INTO v_ename FROM emp WHERE empno=v_empno; RETURN v_ename; END;
public void getenamefun(int empno){ //sql String ename=""; String sql="{?=call getename(?)}"; CallableStatement fun=null; getConnection(); try { fun=conn.prepareCall(sql); fun.setInt(2, empno); fun.registerOutParameter(1, Types.VARCHAR); fun.execute(); ename=fun.getString(1); System.out.println(ename); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
其它的方法與過程一樣,只是多了個返回值類型