需要用到的接口
接口 CallableStatement
JDK文檔對改接口的說明:
public interface CallableStatement
extends
PreparedStatement
用於執行 SQL 存儲過程的接口。JDBC API 提供了一個存儲過程 SQL 轉義語法,該語法允許對所有 RDBMS 使用標准方式調用存儲過程。此轉義語法有一個包含結果參數的形式和一個不包含結果參數的形式。如果使用結果參數,則必須將其注冊為 OUT 參數。其他參數可用於輸入、輸出或同時用於二者。參數是根據編號按順序引用的,第一個參數的編號是 1。
{?= call <procedure-name>[(<arg1>,<arg2>, ...)]} -------存儲函數的sql,第一個?代表返回類型
{call <procedure-name>[(<arg1>,<arg2>, ...)]} -------存儲過程的sql
IN 參數值是使用繼承自 PreparedStatement
的 set
方法設置的。在執行存儲過程之前,必須注冊所有 OUT 參數的類型;它們的值是在執行后通過此類提供的 get
方法獲取的。
CallableStatement
可以返回一個 ResultSet
對象或多個 ResultSet
對象。多個 ResultSet
對象是使用繼承自 Statement
的操作處理的。
為了獲得最大的可移植性,某一調用的 ResultSet
對象和更新計數應該在獲得輸出參數的值之前處理。
例子程序
對oracle的scott/tiger用戶的emp表操作
存儲過程,查詢員工信息
create or replace procedure queryEmpInfo(eno in number, pename out varchar2, psal out number, pjob out varchar2) as begin select ename,sal,job into pename,psal,pjob from emp where empno=eno; end;
存儲函數:
create or replace function queryEmpImcome(eno in number) return number as --變量 psal emp.sal%type; pcomm emp.comm%type; begin select sal,comm into psal,pcomm from emp where empno=eno; return (psal+nvl(pcomm,0))*12; end;
jdbc工具類
package com.lhy.util; import java.sql.*; /** * JDBC工具類 ,一般工具類final。 * 工具類一般不需要new,不需要構造實例。(把構造方法私有)別人就new不了了。 * 此時使用類的方法: * 1是單例模式(復雜點) * 2是提供靜態的public方法。(簡單) * 本例子是簡單的提供public方法實現的。需要靜態方法 * * @author hy * */ public final class JdbcUtils { private static String url ="jdbc:oracle:thin:@127.0.0.1:1521:ORCL"; private static String user ="scott"; private static String passWord = "tiger"; //構造方法私有,別人不能構造,不會有實例出來. private JdbcUtils(){ } /** * 靜態代碼塊,類加載到虛擬機是只執行一次。 */ static { try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() { Connection conn = null; try { conn = DriverManager.getConnection(url,user,passWord); } catch (SQLException e) { e.printStackTrace(); } return conn; } //釋放資源,重載方法。 public static void close(Connection conn) { try { if(conn != null){ conn.close(); conn = null; } }catch(SQLException e){ e.printStackTrace(); } } public static void close(Statement stmt){ try{ if(stmt != null){ stmt.close(); stmt = null; } }catch(SQLException e){ e.printStackTrace(); } } public static void close(ResultSet rs){ try{ if(rs != null){ rs.close(); rs = null; } }catch (SQLException e) { e.printStackTrace(); } } }
測試程序:
public class OracleTest { /** * 測試存儲過程 * create or replace procedure queryEmpInfo(eno in number, pename out varchar2, psal out number, pjob out varchar2) as begin select ename,sal,job into pename,psal,pjob from emp where empno=eno; end; */ @Test public void testProcedure(){ //格式 {call <procedure-name>[(<arg1>,<arg2>, ...)]} String sql = "{call queryEmpInfo(?,?,?,?)}"; Connection conn = null; CallableStatement call = null; try { conn = JdbcUtils.getConnection(); call = conn.prepareCall(sql); //賦值 call.setInt(1, 7839); //對於out參數,聲明 call.registerOutParameter(2, OracleTypes.VARCHAR); call.registerOutParameter(3, OracleTypes.NUMBER); call.registerOutParameter(4, OracleTypes.VARCHAR); //調用 call.execute(); //取值 String name = call.getString(2); double sal = call.getDouble(3); String job = call.getString(4); System.err.println(name); System.err.println(sal); System.err.println(job); }catch(Exception e){ e.printStackTrace(); }finally{ JdbcUtils.close(call); JdbcUtils.close(conn); } } /** * 測試 存儲函數 * create or replace function queryEmpImcome(eno in number) return number as --變量 psal emp.sal%type; pcomm emp.comm%type; begin select sal,comm into psal,pcomm from emp where empno=eno; return (psal+nvl(pcomm,0))*12; end; */ @Test public void testFunction(){ //{?= call <procedure-name>[(<arg1>,<arg2>, ...)]} //第一個?--> 輸出參數,返回值, 第二個 ?-->輸入參數 String sql = "{?= call queryEmpImcome(?)}"; Connection conn = null; CallableStatement call = null; try { conn = JdbcUtils.getConnection(); call = conn.prepareCall(sql); //注冊輸出參數 call.registerOutParameter(1, OracleTypes.NUMBER); //輸入參數 call.setInt(2, 7839); //執行 call.execute(); //取出年收入,注意是get 1 double income = call.getDouble(1); System.err.println("年收入:"+income); } catch (Exception e) { e.printStackTrace(); }finally{ JdbcUtils.close(call); JdbcUtils.close(conn); } } }
運行結果:
性能
Statement < preparedStatement < CallableStatement
在Out參數中使用游標
package:程序包
上邊的例子的存儲過程,輸出參數只有三個,如果輸出參數有很多,或者輸出結果是一個結果集(如查詢一個部門中所有員工的信息),此時就需要在out參數中使用游標
例子:查詢某個部門所有員工的所有信息
使用PLSQL建包
1,新建包
create or replace package MYPACKAGE as type empcursor is ref cursor; procedure queryEmpList(dno in number,empList out empcursor); end MYPACKAGE;
2,新建包體
CREATE OR REPLACE PACKAGE BODY MYPACKAGE AS procedure queryEmpList(dno in number,empList out empcursor) AS BEGIN open empList for select * from emp where deptno=dno; END queryEmpList; END MYPACKAGE;
使用java程序測試:
@Test public void testCursor(){ String sql = "{call MYPACKAGE.queryEmpList(?,?)}"; Connection conn = null; CallableStatement call = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); call = conn.prepareCall(sql); call.setInt(1, 10); call.registerOutParameter(2, OracleTypes.CURSOR); //執行 call.execute(); //取出集合,轉換為OracleCallableStatement,去除游標 rs = ((OracleCallableStatement)call).getCursor(2); while(rs.next()){ String name = rs.getString("ename"); String job = rs.getString("job"); double salary = rs.getDouble("sal"); System.err.println("姓名:"+name+",職位:"+job+",薪水:"+salary); } } catch (Exception e) { e.printStackTrace(); }finally{ JdbcUtils.close(rs); JdbcUtils.close(call); JdbcUtils.close(conn); } }
光標當ResultSet關閉的時候就關閉了