JDBC API 4.2(七):CallableStatement 接口源码分析

1、简述

CallableStatement 是用于执行SQL存储过程的。JDBC API提供了存储过程SQL转义语法,该语法允许所有RDBMS以标准方式调用存储过程。

Connection 接口提供了 prepareCall(String SQL)方法来创建 CallableStatement 对象,以调用数据库存储过程。 CallableStatement对象提供了用于设置其IN和OUT参数的方法,以及用于执行对存储过程的调用的方法。

2、类图

下面的类图显示了 CallableStatement 接口的继承关系:

CallableStatement 使用从 PreparedStatement继承的set方法设置IN参数值。 在执行存储过程之前,必须先注册所有OUT参数的类型。 它们的值在执行后通过此处提供的get方法检索。

一个CallableStatement可以返回一个ResultSet对象或多个ResultSet对象。 使用从Statement继承的操作来处理多个ResultSet对象。

在这里插入图片描述

3、示例

3.1、单个结果集

返回单个结果集的存储过程

DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_single_select` ()
BEGIN
 SELECT * FROM users;
END$$
DELIMITER ;

以下程序演示了如何调用 proc_single_select() 存储过程并生成单个结果集。

 //数据库连接地址
    private final String jdbcUrl = "jdbc:mysql://localhost:3306/lkf_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
    //用户名
    private final String username = "root";
    //密码
    private final String password = "root";
    //调用返回单个结果集存储过程语句
    private final String proc_single_select_sql = "call proc_single_select()";
    //调用返回多个结果集存储过程语句
    private final String proc_multi_select_sql = "call proc_multi_select()";

    public static void main(String[] args) {
        CallableStatementDemo callableStatementDemo = new CallableStatementDemo();
        //返回单个结果集
        callableStatementDemo.singleResultSet();
    }

    /**
     * 返回单个结果集
     */
    public void singleResultSet() {
        try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
             CallableStatement stmt = conn.prepareCall(proc_single_select_sql);
             ResultSet rs = stmt.executeQuery()) {
            while (rs.next()) {
                System.out.println("ID = " + rs.getInt(1) + ", NAME = " + rs.getString(2) + ", Email = " +
                        rs.getString(3) + ", Country = " + rs.getString(4) + ", Password = " + rs.getString(5));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

结果:

ID = 1, NAME = 张三, Email = zhangsan@qq.com, Country = china, Password = 123456
ID = 2, NAME = 李四, Email = lisi@qq.com, Country = china, Password = 123456

3.2、多个结果集

返回多个结果集的存储过程

DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_multi_select` ()
BEGIN
    #查询 id 为 1,按名字去重后的名字
    SELECT DISTINCT NAME FROM users WHERE id = 1;
    #按邮箱去重,并获取去重后的所有邮箱
    SELECT DISTINCT email FROM users;
    #根据id统计用户数量
    SELECT COUNT(id) AS users_count FROM users;
END$$
DELIMITER ;

以下程序演示了如何调用 proc_multi_select() 存储过程并生成多个结果集。

 //数据库连接地址
    private final String jdbcUrl = "jdbc:mysql://localhost:3306/lkf_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
    //用户名
    private final String username = "root";
    //密码
    private final String password = "root";
    //调用返回单个结果集存储过程语句
    private final String proc_single_select_sql = "call proc_single_select()";
    //调用返回多个结果集存储过程语句
    private final String proc_multi_select_sql = "call proc_multi_select()";

    public static void main(String[] args) {
        CallableStatementDemo callableStatementDemo = new CallableStatementDemo();
        //返回单个结果集
//        callableStatementDemo.singleResultSet();
        //返回多个结果集
        callableStatementDemo.multipleResultSet();
    }

    /**
     * 返回多个结果集
     */
    public void multipleResultSet() {
        try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
             CallableStatement stmt = conn.prepareCall(proc_multi_select_sql);) {
            //执行存储过程
            boolean hasRs = stmt.execute();
            System.out.println();
            // 解析名字的结果集
            if (hasRs) {
                try (ResultSet rs = stmt.getResultSet()) {
                    while (rs.next()) {
                        System.out.println("NAME = " + rs.getString(1));
                    }
                }
            }

            // 解析邮箱的结果集
            if (stmt.getMoreResults()) {
                try (ResultSet rs = stmt.getResultSet()) {
                    if (rs.next()) {
                        System.out.println("Email = " + rs.getString(1));
                    }
                }
            }

            // 获取统计的用户数量
            if (stmt.getMoreResults()) {
                try (ResultSet rs = stmt.getResultSet()) {
                    if (rs.next()) {
                        System.out.println("Users count = " + rs.getInt(1));
                    }
                }
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

结果:

NAME = 张三
Email = zhangsan@qq.com
Users count = 2

3.3、带参数的存储过程

存在三种类型的参数:IN,OUT和INOUT。

PreparedStatement对象只使用IN参数。 CallableStatement对象可以使用上面三种类型参数。

参数描述
IN表示调用者传入值(传入值可以是字面量或变量)。创建SQL语句时其参数值是未知的。 使用setXXX()方法将值绑定到IN参数。
OUT表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)。可以使用getXXX()方法从OUT参数中检索值。
INOUT既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)。使用setXXX()方法绑定变量并使用getXXX()方法检索值。

使用 IN 参数,只需遵循适用 于PreparedStatement 对象的相同规则,与绑定的Java数据类型相对应的setXXX()方法。

使用 OUT 和 INOUT 参数时,必须使用 CallableStatement 对象的方法 registerOutParameter()。

registerOutParameter() 方法将JDBC数据类型绑定到存储过程预期返回的数据类型。
当调用存储过程后,可以使用适当的 getXXX() 方法从OUT参数中检索该值。 此方法将检索到的SQL类型的值转换为Java 数据类型。

3.3.1、带有输入参数(IN)的存储过程
DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_getUserByName` (IN p_name VARCHAR(30))
BEGIN
 SELECT * FROM users WHERE NAME=p_name;
END$$
DELIMITER ;

以下示例将调用存储过程 proc_getUserByName 并传入参数,获取对应的记录信息。

 private final String proc_in_select_sql = "call proc_getUserByName(?)";
/**
     * 带有输入参数(IN)的存储过程
     */
    public void procInParam() throws SQLException {
        Connection conn = null;
        CallableStatement stmt = null;
        ResultSet rs = null;
        try {
            //获取连接对象
            conn = DriverManager.getConnection(jdbcUrl, username, password);
            //预编译
            stmt = conn.prepareCall(proc_in_select_sql);
            //传递输入参数
            stmt.setString(1, "张三");
            //执行存储过程
            rs = stmt.executeQuery();
            while (rs.next()) {
                System.out.println("ID = " + rs.getInt(1) + ", NAME = " + rs.getString(2) + ", Email = " +
                        rs.getString(3) + ", Country = " + rs.getString(4) + ", Password = " + rs.getString(5));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                rs.close();
                stmt.close();
                conn.close();
            }
        }
    }

结果:

ID = 1, NAME = 张三, Email = zhangsan@qq.com, Country = china, Password = 123456
3.3.2、带有输出参数(OUT)的存储过程
DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_getUserNumByName` (IN p_name VARCHAR(30),OUT p_userNum INT)
BEGIN
 SELECT COUNT(id) FROM users WHERE NAME=p_name INTO p_userNum;
END$$
DELIMITER ;

以下示例,将调用具有输入和输出参数的存储过程 proc_getUserNumByName 获取满足指定条件的用户数量。

    private final String proc_out_select_sql = "call proc_getUserNumByName(?,?)";
  /**
     * 带有输出参数(OUT)的存储过程
     */
    public void procOutParam() throws SQLException {
        Connection conn = null;
        CallableStatement stmt = null;
        ResultSet rs = null;
        try {
            //获取连接对象
            conn = DriverManager.getConnection(jdbcUrl, username, password);
            //预编译
            stmt = conn.prepareCall(proc_out_select_sql);
            //传递输入参数
            stmt.setString(1, "张三");
            //设置输出参数(注册输出参数)
            /**
             * 参数一: 参数位置
             * 参数二: 存储过程中的输出参数的jdbc类型
             */
            stmt.registerOutParameter(2, Types.INTEGER);
            //执行存储过程
            rs = stmt.executeQuery();
            /**
             * 得到输出参数的值
             * 索引值: 预编译sql中的输出参数的位置
             * getXX方法专门用于获取存储过程中的输出参数
             */
            Integer result = stmt.getInt(2);
            System.out.println(result);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                rs.close();
                stmt.close();
                conn.close();
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值