JDBC 调用自定义函数(常说的存储过程)的步骤

 

平常说的存储过程(Procedure),严格意义上是自定义函数,所以这里以【自定义函数】为名,简称【函数(function)】。

 

package com.joyupx.jdbc;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * 存储过程严格意义上叫【自定义函数】更适宜。
 * 我们这里就叫【自定义函数】,即在数据库中创建的函数,与数据库提供的函数并列。
 */
@Slf4j
public class JDBC_Call_SelfDefinedFunction {

    /**
     * 调用自定义的函数
     */
    @Test
    public void test_call_selfDefined_function () {
        /**
         * 第一步、获取数据库的配置
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是插入数据的 SQL。
         */
        String functionSQL = "{ call `artron-trade`.`firstSDF` }";  // 如果无参数可省略末尾的小括号(小括号表示的函数)。

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        int updatedCount = 0;
        int count = 0;

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        ResultSet resultSet = null;
        try {
            resultSet = callableStatement.getResultSet();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

        /**
         * 第七步、解析结果;具体到此处是解析查询到的结果集。
         */
        if (null != resultSet) {
            try {
                while (resultSet.next()) {
                    count = resultSet.getInt(1);
                    log.info("记录数 = {}", count);
                }
            } catch (Exception e) {
                log.error("迭代结果集出错了!", e);
            }
        }

        /**
         * 第八步:解析结果,具体到这里为显示插入成功的记录数
         */
        if (0 < updatedCount)
            log.info("成功插入 {} 条数据。", updatedCount);

        /**
         * 第九步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

    /**
     * 调用自定义的函数
     */
    @Test
    public void test_call_selfDefined_function_2 () {
        /**
         * 第一步、获取数据库的配置
	 * DELIMITER $$
	*
	 * USE `数据库名`$$
	 *
	 * DROP PROCEDURE IF EXISTS `queryChinaRegion`$$
	 *
	 * CREATE DEFINER=`用户名`@`%` PROCEDURE `queryChinaRegion`(parentId INT)
	 * BEGIN
	 * 		SELECT * FROM `数据库名`.`t_china_region` r WHERE  r.`parentId` = parentId;
	 * 	END$$
	 *
	 * DELIMITER ;
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是插入数据的 SQL。
         */
        String functionSQL = "{ call `数据库名`.`queryChinaRegion`( ? ) }";

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        int id = 1;
        id = 2;
        id = 3;
        id = 38;
        try {
            callableStatement.setInt(1, id);
        } catch (SQLException e) {
            log.error("为参数值填充数据 - 失败!", e);

            return;
        }

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        /**
         * 第八步、获取结果
         */
        ResultSet resultSet = null;
        try {
            resultSet = callableStatement.getResultSet();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

        /**
         * 第九步、解析结果;具体到此处是解析查询到的结果集。
         */
        if (null != resultSet) {
            try {
                while (resultSet.next()) {
                    int id_ = resultSet.getInt(1);
                    int parentId = resultSet.getInt("parentId");
                    byte type = resultSet.getByte("type");
                    String code = resultSet.getString("code");
                    String name = resultSet.getString("name");

                    log.info("ID = {}, \t父 ID = {}, \t类别 = {}, \t编号:{}, \t名称:{}"
                            , id_, parentId, type, code, name);
                }
            } catch (Exception e) {
                log.error("迭代结果集出错了!", e);
            }
        }

        /**
         * 第十步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十一步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

    /**
     * 调用自定义的【加法】函数
     *
     * <note>
     * USE `数据库名`;
     * SELECT DATABASE();
     * DROP PROCEDURE IF EXISTS `add`;
     * CREATE DEFINER=`用户名`@`%` PROCEDURE `add`(number1 INT, number2 INT, OUT result INT) BEGIN SET result = number1 + number2 ; END;
     * SELECT `SPECIFIC_NAME` FROM `INFORMATION_SCHEMA`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '数据库名' AND ROUTINE_TYPE = 'PROCEDURE';
     * </note>
     */
    @Test
    public void test_call_function_add () {
        /**
         * 第一步、获取数据库的配置
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是调用【加法】自定义函数(常被称为存储过程)的 SQL。
         */
        String functionSQL = "{ call `数据库名`.`add`( ?, ?, ? ) }";

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        int number1 = 2;
        int number2 = 3;
        try {
            callableStatement.setInt(1, number1);
            callableStatement.setInt(2, number2);
            callableStatement.registerOutParameter(3, Types.INTEGER);
        } catch (SQLException e) {
            log.error("为参数值填充数据 - 失败!", e);

            return;
        }

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        /**
         * 第八步、获取结果
         */
        int sum = 0;
        try {
            sum = callableStatement.getInt(3);
        } catch (SQLException e) {
            log.error("获取整型结果 - 失败!", e);
        }

        /**
         * 第九步、解析结果;具体到此处是解析查询到的结果集。
         */
        log.info("和 = {}", sum);

        /**
         * 第十步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十一步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

    /**
     * 调用自定义的【减法】函数
     *
     * <note>
     * SHOW PROCEDURE STATUS WHERE db = '数据库名' AND NAME = 'subtract';
     * CREATE PROCEDURE `数据库名`.`subtract`( IN minuend FLOAT, IN subtrahend FLOAT, OUT difference FLOAT ) BEGIN SET difference = minuend - subtrahend ; END;
     * SELECT `SPECIFIC_NAME` FROM `INFORMATION_SCHEMA`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '数据库名' AND ROUTINE_TYPE = 'PROCEDURE';
     * SHOW CREATE PROCEDURE `数据库名`.`subtract`;
     * </note>
     */
    @Test
    public void test_call_function_subtract () {
        /**
         * 第一步、获取数据库的配置
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是调用【减法】自定义函数(常被称为存储过程)的 SQL。
         */
        String functionSQL = "{ call `数据库名`.`subtract`( ?, ?, ? ) }";

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        float minuend = 2.3f;
        float subtrahend = 3.5f;
        try {
            callableStatement.setFloat(1, minuend);
            callableStatement.setFloat(2, subtrahend);
            callableStatement.registerOutParameter(3, Types.FLOAT);
        } catch (SQLException e) {
            log.error("为参数值填充数据 - 失败!", e);

            return;
        }

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        /**
         * 第八步、获取结果
         */
        float difference = 0;
        try {
            difference = callableStatement.getFloat(3);
        } catch (SQLException e) {
            log.error("获取整型结果 - 失败!", e);
        }

        /**
         * 第九步、解析结果;具体到此处是解析查询到的结果集。
         */
        log.info("差 = {}", difference);

        /**
         * 第十步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十一步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

    /**
     * 调用自定义的【乘法】函数
     *
     * <note>
     * SHOW PROCEDURE STATUS WHERE db = '数据库名' AND NAME = 'multiply';
     *
     * CREATE PROCEDURE `数据库名`.`multiply`( IN multiplicand DOUBLE, IN multiplier DOUBLE, OUT product DOUBLE) BEGIN SET product = multiplicand * multiplier ; END;
     *
     * SELECT `SPECIFIC_NAME` FROM `INFORMATION_SCHEMA`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '数据库名' AND ROUTINE_TYPE = 'PROCEDURE';
     * </note>
     */
    @Test
    public void test_call_function_multiply () {
        /**
         * 第一步、获取数据库的配置
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是调用【乘法】自定义函数(常被称为存储过程)的 SQL。
         */
        String functionSQL = "{ call `数据库名`.`multiply`( ?, ?, ? ) }";

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        double multiplicand = 2.3f;
        double multiplier = 3.5f;
        try {
            callableStatement.setDouble(1, multiplicand);
            callableStatement.setDouble(2, multiplier);
            callableStatement.registerOutParameter(3, Types.DOUBLE);
        } catch (SQLException e) {
            log.error("为参数值填充数据 - 失败!", e);

            return;
        }

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        /**
         * 第八步、获取结果
         */
        double product = 0;
        try {
            product = callableStatement.getDouble(3);
        } catch (SQLException e) {
            log.error("获取整型结果 - 失败!", e);
        }

        /**
         * 第九步、解析结果;具体到此处是解析查询到的结果集。
         */
        log.info("积 = {}", product);

        /**
         * 第十步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十一步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

    /**
     * 调用自定义的【除法】函数
     *
     * <note>
     * CREATE PROCEDURE `数据库名`.`divide`( IN dividend BIGINT, IN divisor BIGINT, OUT quotient BIGINT) BEGIN SET quotient = dividend / divisor ; END;
     *
     * SELECT `SPECIFIC_NAME` FROM `INFORMATION_SCHEMA`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '数据库名' AND ROUTINE_TYPE = 'PROCEDURE';
     * </note>
     */
    @Test
    public void test_call_function_divide () {
        /**
         * 第一步、获取数据库的配置
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是调用【除法】自定义函数(常被称为存储过程)的 SQL。
         */
        String functionSQL = "{ call `数据库名`.`divide`( ?, ?, ? ) }";

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        long dividend = 6;
        long divisor = 3;
        try {
            callableStatement.setLong(1, dividend);
            callableStatement.setLong(2, divisor);
            callableStatement.registerOutParameter(3, Types.BIGINT);
        } catch (SQLException e) {
            log.error("为参数值填充数据 - 失败!", e);

            return;
        }

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        /**
         * 第八步、获取结果
         */
        long quotient = 0;
        try {
            quotient = callableStatement.getLong(3);
        } catch (SQLException e) {
            log.error("获取整型结果 - 失败!", e);
        }

        /**
         * 第九步、解析结果;具体到此处是解析查询到的结果集。
         */
        log.info("商 = {}", quotient);

        /**
         * 第十步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十一步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

    /**
     * 调用自定义的【除法】函数
     *
     * <note>
     * SHOW PROCEDURE STATUS WHERE db = '数据库名' AND NAME = 'mod';
     *
     * DELIMITER ;
     *
     * CREATE PROCEDURE `数据库名`.`mod`( IN n1 TINYINT, IN n2 TINYINT, OUT n3 TINYINT) BEGIN SET n3 = n1 % n2 ; END;
     *
     * DELIMITER ;
     *
     * SELECT `SPECIFIC_NAME` FROM `INFORMATION_SCHEMA`.`ROUTINES` WHERE `ROUTINE_SCHEMA` = '数据库名' AND ROUTINE_TYPE = 'PROCEDURE';
     *
     * SHOW CREATE PROCEDURE `数据库名`.`mod`;
     * </note>
     */
    @Test
    public void test_call_function_mod () {
        /**
         * 第一步、获取数据库的配置
         */
        InputStream databaseConfigInputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties") ; // 从【类路径】中获取配置文件
        Properties databaseConfigProperties = new Properties();
        try {
            databaseConfigProperties.load(databaseConfigInputStream);   // 加载【数据库】配置文件
        } catch (IOException e) {
            log.error("加载【数据库配置文件】失败!", e);

            return;
        }

        String username = databaseConfigProperties.getProperty("username");
        String password = databaseConfigProperties.getProperty("password");
        String jdbcURL = databaseConfigProperties.getProperty("jdbcURL");
        String jdbcDriver = databaseConfigProperties.getProperty("jdbcDriver");

        /**
         * 第二步、注册驱动
         */
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException e) {
            log.error("没有找到数据库的驱动!", e);

            return ;
        }

        /**
         * 第三步、获取数据库的连接
         */
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(jdbcURL, username, password);
        } catch (SQLException e) {
            log.error("获取 Java 程序与数据库服务端程序的连接对象 - 失败!", e);

            return ;
        }

        /**
         * 第四步、编写(与业务密切相关的)SQL,具体到这里是调用【求余】自定义函数(常被称为存储过程)的 SQL。
         */
        String functionSQL = "{ call `数据库名`.`mod`( ?, ?, ? ) }";

        /**
         * 第五步、创建 SQL 的【可调用的声明】对象,用于对 SQL 语句做准备工作。
         */
        CallableStatement callableStatement = null;
        try {
            callableStatement = connection.prepareCall(functionSQL);
        } catch (SQLException e) {
            log.error("创建 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第六步:为参数值填充参数据。
         */
        byte n1 = 99;
        byte n2 = 10;
        try {
            callableStatement.setByte(1, n1);
            callableStatement.setByte(2, n2);
            callableStatement.registerOutParameter(3, Types.TINYINT);
        } catch (SQLException e) {
            log.error("为参数值填充数据 - 失败!", e);

            return;
        }

        /**
         * 第七步、执行 SQL 更新操作并获取受影响的行数,具体到此处为执行向数据表中添加数据操作并获取添加成功的行数。
         */
        try {
            callableStatement.execute();
        } catch (SQLException e) {
            log.error("执行 SQL 更新(具体到这里是向数据表中添加数据) - 失败!", e);

            return;
        }

        /**
         * 第八步、获取结果
         */
        byte n3 = 0;
        try {
            n3 = callableStatement.getByte(3);
        } catch (SQLException e) {
            log.error("获取整型结果 - 失败!", e);
        }

        /**
         * 第九步、解析结果;具体到此处是解析查询到的结果集。
         */
        log.info("余 = {}", n3);

        /**
         * 第十步、关闭 SQL 的【可调用的声明】对象。
         */
        try {
            callableStatement.close();
        } catch (SQLException e) {
            log.error("关闭 SQL【可调用的声明】对象 - 失败!", e);

            return;
        }

        /**
         * 第十一步、断开与数据库的连接。
         */
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("关闭 Java 程序与数据库服务端程序的连接 - 失败!", e);
        }
    }

}

  

posted @ 2023-04-19 08:54  hapday  阅读(151)  评论(0编辑  收藏  举报