JDBC調用存儲過程之實例講解
【說明】:本文主要講解使用JDBC調用存儲過程的各種方案,數據庫使用Oracle(其他數據庫類似)涉及到的數據表均為Oracle自帶的Scott帳號的數據表。
【引言】:存儲過程是數據庫使用的重要技術之一,以其高效率、高安全性見長,而JDBC調用存儲過程也是Java程序員必掌握的技能之一。JDBC調用存儲過程主要使用CallableStatement接口,而對於輸入(in)和輸出(out)參數的處理也比較復雜,本文使用案例並有詳細注解來說明各種情況。
一、調用帶輸入\輸出參數的存儲過程
1. 建立存儲過程
-- 輸入職工號(zgh)、輸出姓名(xm)和工資(gz) create or replace procedure getNameSalByNo(zgh in emp.empno%type,xm out emp.ename%type,gz out emp.sal%type) is begin select ename,sal into xm,gz from emp where empno=zgh; end; |
2. JDBC的調用
package com.tjxz.proc; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Types; public class CallProcedure { /** * 調用帶普通參數的存儲過程 * @author icer * @web http://www.tjxz.com */ public static void main(String[] args) { // 設置JDBC參數信息 String url = "jdbc:oracle:thin:@localhost:1521:orcl"; String uid = "scott"; String pwd = "triger"; try { // 創建接受返回值的變量(姓名:xm,工資:gz) String xm=""; float gz=0.0f; /* * 准備SQL語句 |
* 格式為: {call 存儲過程名{?,?,?}} * 括號中的問號和存儲過程參數進行匹配 */ String sql = "{call getNameSalByNo(?,?,?)}"; // 加載驅動程序 Class.forName("oracle.jdbc.driver.OracleDriver"); // 獲取連接對象 Connection con = DriverManager.getConnection(url, uid, pwd); // 獲取執行對象 CallableStatement cst = con.prepareCall(sql); // 執行之前要使用setXXX來替換SQL語句中的問號參數 cst.setInt(1, 7788); // 注冊輸出參數類型(注意索引要和問號的位置對應) cst.registerOutParameter(2, Types.VARCHAR); cst.registerOutParameter(3, Types.FLOAT); // 執行SQL命令 cst.execute(); // 提取輸出參數 xm = cst.getString(2); gz = cst.getFloat(3); // 控制台輸出 System.out.println("姓名:" + xm); System.out.println("工資:" + gz); // 關閉相關對象 cst.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } |
3. 輸出結果
姓名:SCOTT 工資:3000.0 |
二、調用帶參數及返回值的函數
1. 建立函數
-- 輸入職工號(zgh),輸出工資(gz) create or replace function getSalByNo(zgh in emp.empno%type) return emp.sal%type is |
gz emp.sal%type; begin select sal into gz from emp where empno=zgh; return gz; exception when others then return -1; end; |
2. JDBC調用
package com.tjxz.proc; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Types; public class CallFunction { /** * 調用帶參數及返回值的函數 * @author icer * @web http://www.tjxz.com */ public static void main(String[] args) { // 設置JDBC參數信息 String url = "jdbc:oracle:thin:@localhost:1521:orcl"; String uid = "scott"; String pwd = "triger"; try { // 創建接受返回值的變量(工資:gz) float gz=0.0f; /* * 准備SQL語句 * 格式為: {?=call 函數名{?,?,?}} * 括號中的問號和函數參數進行匹配,使用?=接受返回值 */ String sql = "{?=call getSalByNo(?)}"; // 加載驅動程序 Class.forName("oracle.jdbc.driver.OracleDriver"); // 獲取連接對象 Connection con = DriverManager.getConnection(url, uid, pwd); // 獲取執行對象 CallableStatement cst = con.prepareCall(sql); |
// 執行之前要使用setXXX來替換SQL語句中的問號參數 cst.setInt(2, 7788); // 注冊輸出參數類型(注意索引要和問號的位置對應) cst.registerOutParameter(1, Types.FLOAT); // 執行SQL命令 cst.execute(); // 提取輸出參數 gz = cst.getFloat(1); // 控制台輸出 System.out.println("工資:" + gz); // 關閉相關對象 cst.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } |
3. 輸出結果
工資:3000.0 |
三、調用輸出參數為游標的存儲過程
1. 創建存儲過程
-- 輸入部門編號(dno),輸出此部門的所職工信息 /*說明:sys_refcursor為系統已定義的動態游標類型聲明*/ create or replace procedure getEmpByDeptno(dno in emp.deptno%type,emps out sys_refcursor) is begin open emps for select * from emp where deptno=dno; end; |
2. JDBC調用
package com.tjxz.proc; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import oracle.jdbc.internal.OracleTypes; |
public class CallProcedureOfCursor { /** * 調用輸出參數為游標的存儲過程 * @author icer * @web http://www.tjxz.com */ public static void main(String[] args) { // 設置JDBC參數信息 String url = "jdbc:oracle:thin:@localhost:1521:orcl"; String uid = "scott"; String pwd = "triger"; try { /* * 准備SQL語句 格式為: {call 存儲過程名{?,?,?}} * 括號中的問號和存儲過程參數進行匹配 */ String sql = "{call getEmpByDeptno(?,?)}"; // 加載驅動程序 Class.forName("oracle.jdbc.driver.OracleDriver"); // 獲取連接對象 Connection con = DriverManager.getConnection(url, uid, pwd); // 獲取執行對象 CallableStatement cst = con.prepareCall(sql); // 執行之前要使用setXXX來替換SQL語句中的問號參數 cst.setInt(1, 10); // 注冊輸出參數類型(注意索引要和問號的位置對應) cst.registerOutParameter(2, OracleTypes.CURSOR); // 執行SQL命令 cst.execute(); // 提取輸出參數(輸出游標使用ResultSet類型接收) ResultSet rst = (ResultSet) cst.getObject(2); // 控制台輸出 System.out.println("EMPNO\tENAME"); System.out.println("----------------------"); while (rst.next()) { System.out.println(rst.getInt("empno") + "\t" + rst.getString("ename")); } // 關閉相關對象 rst.close(); cst.close(); con.close(); } catch (ClassNotFoundException e) { |
e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } |
3. 輸出結果
EMPNO ENAME ---------------------- 7782 CLARK 7839 KING 7934 MILLER |
四、 調用返回值為游標的函數
1. 創建函數
-- 輸入部門編號(dno),返回此部門的所職工信息 /*說明:sys_refcursor為系統已定義的動態游標類型聲明*/ create or replace function getEmpsByDeptno(dno in emp.deptno%type) return sys_refcursor is emps sys_refcursor; begin open emps for select * from emp where deptno=dno; return emps; end; |
2. JDBC調用
package com.tjxz.proc; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import oracle.jdbc.internal.OracleTypes; public class CallFunctionOfCursor { /** * 調用返回值為游標的函數 * * @author icer * @web http://www.tjxz.com */ |
public static void main(String[] args) { // 設置JDBC參數信息 String url = "jdbc:oracle:thin:@localhost:1521:orcl"; String uid = "scott"; String pwd = "triger"; try { /* * 准備SQL語句,格式為: {?=call 函數名{?,?,?}} * 括號中的問號和函數參數進行匹配,使用?=接受返回值 */ String sql = "{?=call getEmpsByDeptno(?)}"; // 加載驅動程序 Class.forName("oracle.jdbc.driver.OracleDriver"); // 獲取連接對象 Connection con = DriverManager.getConnection(url, uid, pwd); // 獲取執行對象 CallableStatement cst = con.prepareCall(sql); // 執行之前要使用setXXX來替換SQL語句中的問號參數 cst.setInt(2, 10); // 注冊輸出參數類型(注意索引要和問號的位置對應) cst.registerOutParameter(1, OracleTypes.CURSOR); // 執行SQL命令 cst.execute(); // 提取輸出參數(輸出游標使用ResultSet類型接收) ResultSet rst = (ResultSet) cst.getObject(1); // 控制台輸出 System.out.println("EMPNO\tENAME"); System.out.println("----------------------"); while (rst.next()) { System.out.println(rst.getInt("empno") + "\t" + rst.getString("ename")); } // 關閉相關對象 rst.close(); cst.close(); con.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } |
3. 輸出結果同上
五、 調用自聲明游標類型的存儲過程
1. 創建存儲過程
/*不使用系統已聲明的動態游標類型,在程序包中自己聲明動態游標類型*/ -- 創建程序包首部,聲明游標和存儲過程 create or replace package pk_scott as -- 聲明動態游標類型 type dcur is ref cursor; -- 聲明存儲過程 procedure getEmpByDeptno(dno in emp.deptno%type,emps out dcur); end; -- 創建程序包體,並實現存儲過程 create or replace package body pk_scott as -- 實現存儲過程,輸入部門號返回此部門所有雇員信息 procedure getEmpByDeptno(dno in emp.deptno%type,emps out dcur) is begin open emps for select * from emp where deptno=dno; end; end; |
2. JDBC調用:同上
3. 顯示結果:同上
補充:如果調用不帶任何參數的存儲過程格式為{call 存儲過程名}。