JDBC 执行存储过程

本文主要是总结 如何实现 JDBC调用Oracle的存储过程,从以下情况分别介绍:

  • [1]、只有输入IN参数,没有输出OUT参数
  • [2]、既有输入IN参数,也有输出OUT参数,输出是简单值(非列表)
  • [3]、既有输入IN参数,也有输出OUT参数,输出是列表
  • [4]、输入输出参数是同一个(IN OUT)

【准备工作】

  创建一个测试表TMP_MICHAEL ,并插入数据,SQL如下:

create table TMP_MICHAEL
(
  USER_ID    VARCHAR2(20),
  USER_NAME  VARCHAR2(10),
  SALARY     NUMBER(8,2),
  OTHER_INFO VARCHAR2(100)
)

insert into TMP_MICHAEL (USER_ID, USER_NAME, SALARY, OTHER_INFO)
values ('michael', 'Michael', 5000, 'http://sjsky.iteye.com');
insert into TMP_MICHAEL (USER_ID, USER_NAME, SALARY, OTHER_INFO)
values ('zhangsan', '张三', 10000, null);
insert into TMP_MICHAEL (USER_ID, USER_NAME, SALARY, OTHER_INFO)
values ('aoi_sola', '苍井空', 99999.99, 'twitter account');
insert into TMP_MICHAEL (USER_ID, USER_NAME, SALARY, OTHER_INFO)
values ('李四', '李四', 2500, null);
Oracle jdbc 常量:

private final static String DB_DRIVER = "oracle.jdbc.driver.OracleDriver";
    private final static String DB_CONNECTION = "jdbc:oracle:thin:@127.0.0.1:1521:Ora11g";
    private final static String DB_NAME = "mytest";
    private final static String DB_PWd = "111111";
[一]、只有输入IN参数,没有输出OUT参数

存储过程 TEST_MICHAEL_NOOUT 的相关SQL:

CREATE OR REPLACE PROCEDURE TEST_MICHAEL_NOOUT(P_USERID    IN VARCHAR2,
                                               P_USERNAME  IN VARCHAR2,
                                               P_SALARY    IN NUMBER,
                                               P_OTHERINFO IN VARCHAR2) IS
BEGIN

  INSERT INTO TMP_MICHAEL
    (USER_ID, USER_NAME, SALARY, OTHER_INFO)
  VALUES
    (P_USERID, P_USERNAME, P_SALARY, P_OTHERINFO);

END TEST_MICHAEL_NOOUT;
调用代码如下:

/**
     * 测试调用存储过程:无返回值
     * @blog http://sjsky.iteye.com
     * @author Michael
     * @throws Exception
     */
    public static void testProcNoOut() throws Exception {
        System.out.println("-------  start 测试调用存储过程:无返回值");
        Connection conn = null;
        CallableStatement callStmt = null;
        try {
            Class.forName(DB_DRIVER);
            conn = DriverManager.getConnection(DB_CONNECTION, DB_NAME, DB_PWd);
            // 存储过程 TEST_MICHAEL_NOOUT 其实是向数据库插入一条数据
            callStmt = conn.prepareCall("{call TEST_MICHAEL_NOOUT(?,?,?,?)}");

            // 参数index从1开始,依次 1,2,3...
            callStmt.setString(1, "jdbc");
            callStmt.setString(2, "JDBC");
            callStmt.setDouble(3, 8000.00);
            callStmt.setString(4, "http://sjsky.iteye.com");
            callStmt.execute();
            System.out.println("-------  Test End.");
        } catch (Exception e) {
            e.printStackTrace(System.out);
        } finally {
            if (null != callStmt) {
                callStmt.close();
            }
            if (null != conn) {
                conn.close();
            }
        }
    }

[二]、既有输入IN参数,也有输出OUT参数,输出是简单值(非列表)

存储过程 TEST_MICHAEL 的SQL如下:

CREATE OR REPLACE PROCEDURE TEST_MICHAEL(P_USERID IN VARCHAR2,
                                         P_SALARY IN NUMBER,
                                         P_COUNT  OUT NUMBER) IS
  V_SALARY NUMBER := P_SALARY;
BEGIN
  IF V_SALARY IS NULL THEN
    V_SALARY := 0;
  END IF;
  IF P_USERID IS NULL THEN
    SELECT COUNT(*)
      INTO P_COUNT
      FROM TMP_MICHAEL T
     WHERE T.SALARY >= V_SALARY;
  ELSE
    SELECT COUNT(*)
      INTO P_COUNT
      FROM TMP_MICHAEL T
     WHERE T.SALARY >= V_SALARY
       AND T.USER_ID LIKE '%' || P_USERID || '%';
  END IF;
  DBMS_OUTPUT.PUT_LINE('v_count=:' || P_COUNT);
END TEST_MICHAEL;
调用程序如下:

/**
     * 测试调用存储过程:返回值是简单值非列表
     * @blog http://sjsky.iteye.com
     * @author Michael
     * @throws Exception
     */
    public static void testProcOutSimple() throws Exception {
        System.out.println("-------  start 测试调用存储过程:返回值是简单值非列表");
        Connection conn = null;
        CallableStatement stmt = null;
        try {
            Class.forName(DB_DRIVER);
            conn = DriverManager.getConnection(DB_CONNECTION, DB_NAME, DB_PWd);

            stmt = conn.prepareCall("{call TEST_MICHAEL(?,?,?)}");

            stmt.setString(1, "");
            stmt.setDouble(2, 3000);

            // out 注册的index 和取值时要对应
            stmt.registerOutParameter(3, Types.INTEGER);
            stmt.execute();

            // getXxx(index)中的index 需要和上面registerOutParameter的index对应
            int i = stmt.getInt(3);
            System.out.println("符号条件的查询结果 count := " + i);
            System.out.println("-------  Test End.");
        } catch (Exception e) {
            e.printStackTrace(System.out);
        } finally {
            if (null != stmt) {
                stmt.close();
            }
            if (null != conn) {
                conn.close();
            }
        }
    }
测试程序就是查询薪水3000以上人员的数量 ,运行结果如下:
------- start 测试调用存储过程:返回值是简单值非列表
符号条件的查询结果 count := 4
------- Test End.

[三]、既有输入IN参数,也有输出OUT参数,输出是列表

首先需要创建PACKAGE TEST_PKG_CURSOR 的SQL如下:

CREATE OR REPLACE PACKAGE TEST_PKG_CURSOR IS

  -- Author  : MICHAEL  http://sjsky.iteye.com
  TYPE TEST_CURSOR IS REF CURSOR;

END TEST_PKG_CURSOR;
再创建存储过程 TEST_P_OUTRS 的SQL如下:

CREATE OR REPLACE PROCEDURE TEST_P_OUTRS(P_SALARY IN NUMBER,
                                         P_OUTRS  OUT TEST_PKG_CURSOR.TEST_CURSOR) IS
  V_SALARY NUMBER := P_SALARY;
BEGIN
  IF P_SALARY IS NULL THEN
    V_SALARY := 0;
  END IF;
  OPEN P_OUTRS FOR
    SELECT * FROM TMP_MICHAEL T WHERE T.SALARY > V_SALARY;
END TEST_P_OUTRS;
 调用存储过程的代码如下:

/**
     * 测试调用存储过程:有返回值且返回值为列表的
     * @blog http://sjsky.iteye.com
     * @author Michael
     * @throws Exception
     */
    public static void testProcOutRs() throws Exception {
        System.out.println("-------  start 测试调用存储过程:有返回值且返回值为列表的");
        Connection conn = null;
        CallableStatement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName(DB_DRIVER);
            conn = DriverManager.getConnection(DB_CONNECTION, DB_NAME, DB_PWd);

            stmt = conn.prepareCall("{call TEST_P_OUTRS(?,?)}");

            stmt.setDouble(1, 3000);
            stmt.registerOutParameter(2, OracleTypes.CURSOR);
            stmt.execute();

            // getXxx(index)中的index 需要和上面registerOutParameter的index对应
            rs = (ResultSet) stmt.getObject(2);
            // 获取列名及类型
            int colunmCount = rs.getMetaData().getColumnCount();
            String[] colNameArr = new String[colunmCount];
            String[] colTypeArr = new String[colunmCount];
            for (int i = 0; i < colunmCount; i++) {
                colNameArr[i] = rs.getMetaData().getColumnName(i + 1);
                colTypeArr[i] = rs.getMetaData().getColumnTypeName(i + 1);
                System.out.print(colNameArr[i] + "(" + colTypeArr[i] + ")"
                        + " | ");
            }
            System.out.println();
            while (rs.next()) {
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < colunmCount; i++) {
                    sb.append(rs.getString(i + 1) + " | ");
                }
                System.out.println(sb);
            }
            System.out.println("------- Test Proc Out is ResultSet end. ");

        } catch (Exception e) {
            e.printStackTrace(System.out);
        } finally {
            if (null != rs) {
                rs.close();
            }
            if (null != stmt) {
                stmt.close();
            }
            if (null != conn) {
                conn.close();
            }
        }
    }

 运行结果如下:

------- start 测试调用存储过程:有返回值且返回值为列表的
USER_ID(VARCHAR2) | USER_NAME(VARCHAR2) | SALARY(NUMBER) | OTHER_INFO(VARCHAR2) | 
michael | Michael | 5000 | null | 
zhangsan | 张三 | 10000 | null | 
aoi_sola | 苍井空 | 99999.99 | null | 
jdbc | JDBC | 8000 | http://sjsky.iteye.com | 
------- Test Proc Out is ResultSet end.

[四]、输入输出参数是同一个(IN OUT)

创建存储过程TEST_P_INOUT 的SQL如下:

CREATE OR REPLACE PROCEDURE TEST_P_INOUT(P_USERID IN VARCHAR2,
                                         P_NUM    IN OUT NUMBER) IS
  V_COUNT  NUMBER;
  V_SALARY NUMBER := P_NUM;
BEGIN
  IF V_SALARY IS NULL THEN
    V_SALARY := 0;
  END IF;

  SELECT COUNT(*)
    INTO V_COUNT
    FROM TMP_MICHAEL
   WHERE USER_ID LIKE '%' || P_USERID || '%'
     AND SALARY >= V_SALARY;
  P_NUM := V_COUNT;
END TEST_P_INOUT;
 调用存储过程的代码:

/**
     * 测试调用存储过程: INOUT同一个参数:
     * @blog http://sjsky.iteye.com
     * @author Michael
     * @throws Exception
     */
    public static void testProcInOut() throws Exception {
        System.out.println("-------  start 测试调用存储过程:INOUT同一个参数");
        Connection conn = null;
        CallableStatement stmt = null;
        try {
            Class.forName(DB_DRIVER);
            conn = DriverManager.getConnection(DB_CONNECTION, DB_NAME, DB_PWd);

            stmt = conn.prepareCall("{call TEST_P_INOUT(?,?)}");

            stmt.setString(1, "michael");
            stmt.setDouble(2, 3000);

            // 注意此次注册out 的index 和上面的in 参数index 相同
            stmt.registerOutParameter(2, Types.INTEGER);
            stmt.execute();

            // getXxx(index)中的index 需要和上面registerOutParameter的index对应
            int count = stmt.getInt(2);
            System.out.println("符号条件的查询结果 count := " + count);
            System.out.println("-------  Test End.");
        } catch (Exception e) {
            e.printStackTrace(System.out);
        } finally {
            if (null != stmt) {
                stmt.close();
            }
            if (null != conn) {
                conn.close();
            }
        }
    }
运行结果如下:
------- start 测试调用存储过程:INOUT同一个参数
符号条件的查询结果 count := 1
------- Test End.

你可能感兴趣的:(java)