[疯狂Java]JDBC:CallableStatement执行存储过程

时间:2021-07-05 11:56:00

1. 简单的存储过程的创建:

    1) 一个MySQL中典型的存储过程的创建:

delimiter
create procedure add(a int, b int, out sum int)
begin
set sum = a + b;
end;
!!存储过程类似于C/Java中的函数和方法,具有参数传入,但是没有返回值;

!!返回值是通过输出参数来获取的,输出参数之前要用out关键字限定,存储过程执行完毕后就可以从输出参数中取出想要的计算结果;

!!具体的存储过程的有关知识在这里就不详解了;


2. JDBC执行存储过程:

    1) 首先存储过程必须现在数据库中写好并编译好,以供其它应用程序调用它,毕竟存储过程跟数据包、索引、视图等都是数据库对象,地位都是相同的,因此在应用程序中查询一个表和调用一个存储过程的地位是相同;

    2) 要调用一个存储过程就必须先获取一个”调用存储过程的句柄“,它也是一种Statement,也可以通过Connection(conn)来获取,就是CallableStatement;

    3) 获取CallableStatement:CallableStatement Connection.prepareCall(String sql);

         i. 可以看到得到的调用句柄同样是预编译好的,只要方法是prepare作为前缀的都表示预编译,而已create作为前缀的都表示是直接提交执行的(即每次提交都要编译);

         ii. 毕竟,存储过程是一种数据库对象保存在数据库中,因此这里唯一能提交的SQL语句也就是调用存储过程的语句了,因此这里预编译的SQL语句必须是call语句;

         iii. 调用存储过程的call语句语法格式是:{call 过程名(参数1, 参数2...)};  // 注意!外面一定要加花括号

         iv. 既然是预编译的那就允许使用占位符,这是理所应当的,一般都是形参部分使用占位符代替,后期用具体的参数填写它;

         v. 示例(获取一个预编译的调用存储过程的SQL语句):CallableStatement cstmt = conn.prepareCall("{call add(?, ?, ?)}");

    4) 后期可以通过CallableStatement的setXxx方法来填写占位符,和PrepareStatement的setXxx一样,只不过CallableStatement还提供了一个根据形参名填写占位符的重载版本:

         i. void setXxx(int parameterIndex, Xxx x);   // 正常版本

         ii. void setXxx(String parameterName, Xxx x);   // 根据参数名来填写占位符

!!因为存储过程定义中参数都是有名字的,就是根据这个名字来填写占位符;

    5) 那该如何获取输出参数的值呢?

         i. 在调用存储过程时传入输入参数的值是理所应当的(同时也是必须的),上面的setXxx方法就是传入输入参数的值;

         ii. 而输出参数也可以作为输入参数,只不过输入时当做输入参数处理,当存储过程计算完之后会把结果覆盖到输出参数输出罢了,因此输出参数也可以用setXxx传值;

         iii. 但是一般符合规范的做法是不要给输出参数传至,即调用用JDBC调用存储过程时不要为输出参数传值(输出参数最好是纯当返回值使用),而只在存储过程计算完毕之后再取出输出参数的值;

         iv. JDBC要求必须先给CallableStatement对象注册输出参数(即提前现高数CallableStatement谁是输出参数,这样好让它先做好准备),使用registerOutParameter进行注册:void CallableStatement.registerOutParameter(String parameterName | int parameterIndex, SQLType sqlType);

              a. 该函数可以用参数名parameterName或者参数索引parameterIndex(从1开始)来定位谁是输出参数;

              b. SQLType是SQL的数据类型(即int、varchar、blob等),因此sqlType即使指该参数的类型应该是SQL数据类型里的哪种;

              c. 这里SQLType只是一个抽象接口,实际中应该是用其实现类Types(位于sql包中),其中定义了几乎所有标准SQL支持的类型,例如INTERGER、DOUBLE、BLOB等;

              d. 示例:cstmt.registerOutParameter(3, Types.INTEGER);  // 告诉cstmt第三个参数是输出参数,其在SQL中的类型是int型的

         v. 当执行完存储过程后就可以用getXxx方法获取输出参数的值了,getXxx和setXxx是相对应的,其也有两个重载版本,一个是根据索引指定参数,另一个是根据参数名指定参数:Xxx CallableStatement.getXxx(int parameterIndex | String parameterName);

!!注意:一定要用该方法获取输出参数的值,不要获取输入参数的值!

    6) 执行存储过程:直接调用CallableStatement的execute方法即可,既然是预编译的必然无参,而且也没有其他版本(executeUpdate、executeQuery),因为CallableStatement就是专门用来调用存储过程的,因此就只有execute方法一种:boolean CallableStatement.execute();


3. 示例:一个完整的执行存储过程的例子

CallableStatement cstmt = conn.prepareCall("{call add_pro(?, ?, ?)}"); // 创建一个预编译的存储过程调用SQL语句
cstmt.setInt(1, 5); // 参数索引指定,第一个参数
cstmt.setInt("b", 6); // 参数名指定
cstmt.registerOutParameter(3, Types.INTEGER); // 注册输出参数以及其SQL类型
cstmt.execute(); // 执行存储过程
int ret = cstmt.getInt(3); // 计算完成后获取输出参数的值