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

Posted hapday

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
        
    


  

MyBatis - 使用自定义对象调用存储过程

我想在Spring-boot应用程序中使用Mybatis 3调用带有自定义对象的oracle存储过程。我没有任何关于如何做的例子。我已经有一个使用标准JDBC调用该过程的方法,我想将其转换为MyBatis。

public void perform() throws DialectException 
    PreparedStatement ps=null;
    ResultSet rs=null;
    UnitBean unitBean;
    unitList = new ArrayList();
    CallableStatement cs=null;
    Connection oraConn;
    try 
        oraConn = ((PooledConnection)conn).getPhysicalConnection();
        cs = oraConn.prepareCall(sqlSvc.getSqlStatement("GIB_INTERFACE.list"));
        StructDescriptor structDescStdUntTyp = StructDescriptor.createDescriptor("STD_UNT_TYP", oraConn);
        StructDescriptor structDescAdvUntTyp = StructDescriptor.createDescriptor("ADV_UNT_TYP", oraConn);
        ArrayDescriptor descriptorVarcharVarrayType = ArrayDescriptor.createDescriptor("VARCHAR_VARRAY_TYPE", oraConn);
        Object[] attributesStdUntTyp = new Object[9];
        Object[] attributesAdvUntTyp = new Object[15];
        ARRAY tecArray = null;
        ARRAY geSerialNumberArray = null;
        ARRAY oemSerialNumberArray = null;
        ARRAY jobNumberArray = null;
        ARRAY unitStatusArray = null;
        ARRAY equipmentArray = null;
        ARRAY contractualStatusArray = null;
        ARRAY trainServiceTypeArray = null;
        ARRAY fuelTypeArray = null;
        ARRAY combustionSystemArray = null;
        ARRAY equipmentLocationArray = null;

        tecArray = populateUnitDataSet(filterUnitBean,  "getTechnologyInput", "getTechnologyInput", oraConn, descriptorVarcharVarrayType);

        geSerialNumberArray = populateUnitDataSet(filterUnitBean,  "getGeSerialInput", "getGeSerialOutput", oraConn, descriptorVarcharVarrayType);

        oemSerialNumberArray = populateUnitDataSet(filterUnitBean,  "getOemSerialNumberInput", "getOemSerialNumberOutput", oraConn, descriptorVarcharVarrayType);

        jobNumberArray = populateUnitDataSet(filterUnitBean,  "getJobNumberInput", "getJobNumberOutput", oraConn, descriptorVarcharVarrayType);

        unitStatusArray = populateUnitDataSet(filterUnitBean,  "getUnitStatusInput", "getUnitStatusInput", oraConn, descriptorVarcharVarrayType);

        equipmentArray = populateUnitDataSet(filterUnitBean,  "getEquipmentInput", "getEquipmentInput", oraConn, descriptorVarcharVarrayType);

        contractualStatusArray = populateUnitDataSet(filterUnitBean,  "getContractualStatusInput", "getContractualStatusInput", oraConn, descriptorVarcharVarrayType);

        trainServiceTypeArray = populateUnitDataSet(filterUnitBean,  "getTrainServiceTypeInput", "getTrainServiceTypeInput", oraConn, descriptorVarcharVarrayType);

        fuelTypeArray = populateUnitDataSet(filterUnitBean,  "getFuelTypeInput", "getFuelTypeInput", oraConn, descriptorVarcharVarrayType);

        combustionSystemArray = populateUnitDataSet(filterUnitBean,  "getCombustionSystemInput", "getCombustionSystemInput", oraConn, descriptorVarcharVarrayType);

        equipmentLocationArray = populateUnitDataSet(filterUnitBean,  "getEquipmentLocationInput", "getEquipmentLocationInput", oraConn, descriptorVarcharVarrayType);


        STRUCT standardUnit;
        attributesStdUntTyp[0] = geSerialNumberArray;
        attributesStdUntTyp[1] = oemSerialNumberArray;
        attributesStdUntTyp[2] = Utility.resolveNull(filterUnitBean.getCustomer());
        attributesStdUntTyp[3] = Utility.resolveNull(filterUnitBean.getSiteName());
        attributesStdUntTyp[4] = jobNumberArray;
        attributesStdUntTyp[5] = unitStatusArray;
        attributesStdUntTyp[6] = equipmentArray;
        attributesStdUntTyp[7] = tecArray;
        attributesStdUntTyp[8] = Utility.resolveNull(filterUnitBean.getEquipmentName());

        standardUnit = new STRUCT(structDescStdUntTyp,oraConn,attributesStdUntTyp);

        STRUCT advancedUnit;
        attributesAdvUntTyp[0] = Utility.resolveNull(filterUnitBean.getRelatedMachines());
        attributesAdvUntTyp[1] = Utility.resolveNull(filterUnitBean.getGlobalCustomer());
        attributesAdvUntTyp[2] = contractualStatusArray;
        attributesAdvUntTyp[3] = Utility.resolveNull(filterUnitBean.getWarranty());
        attributesAdvUntTyp[4] = Utility.resolveNull(filterUnitBean.getWhru());
        attributesAdvUntTyp[5] = Utility.resolveNull(filterUnitBean.getRmdAvailable());
        attributesAdvUntTyp[6] = Utility.resolveNull(null);
        attributesAdvUntTyp[7] = Utility.resolveNull(filterUnitBean.getPilotAvailable());
        attributesAdvUntTyp[8] = Utility.resolveNull(filterUnitBean.getExtendorKit());
        attributesAdvUntTyp[9] = null;
        attributesAdvUntTyp[10] = trainServiceTypeArray;
        attributesAdvUntTyp[11] = fuelTypeArray;
        attributesAdvUntTyp[12] = combustionSystemArray;
        attributesAdvUntTyp[13] = equipmentLocationArray;
        attributesAdvUntTyp[14] = Utility.resolveNull(filterUnitBean.getRelatedOem());

        advancedUnit = new STRUCT(structDescAdvUntTyp,oraConn,attributesAdvUntTyp);

        cs.registerOutParameter(1,OracleTypes.CURSOR);
        cs.setObject(2,standardUnit);
        cs.setObject(3,advancedUnit);

        cs.setInt(4,Integer.parseInt((lowerBound!=null)?lowerBound:"0")+Integer.parseInt(maxPageItems));
        cs.setInt(5,Integer.parseInt((lowerBound!=null)?lowerBound:"0"));
        cs.registerOutParameter(6,OracleTypes.NUMBER);
        cs.execute();

        rs = (ResultSet) cs.getObject(1);

        int count = cs.getInt(6);
        itemsCount = String.valueOf(count);

        while(rs.next())
            unitBean = new UnitBean();

            unitBean.setGibSerialNumber(Utility.resolveNull(rs.getString("GIB_SERIAL_NUMBER")));
            unitBean.setOemSerialNumber(Utility.resolveNull(rs.getString("OEM_SERIAL_NUMBER")));
            unitBean.setSiteCustomerDuns(Utility.resolveNull(rs.getString("SITE_CUSTOMER_DUNS")));
            unitBean.setSiteCustomerName(Utility.resolveNull(rs.getString("SITE_CUSTOMER_NAME")));
            unitBean.setSiteCustomerCountry(Utility.resolveNull(rs.getString("SITE_CUSTOMER_COUNTRY")));
            unitBean.setSiteNameAlias(Utility.resolveNull(rs.getString("SITE_NAME_ALIAS")));
            unitBean.setGloCustomerDuns(Utility.resolveNull(rs.getString("GLO_CUSTOMER_DUNS")));
            unitBean.setGloCustomerName(Utility.resolveNull(rs.getString("GLO_CUSTOMER_NAME")));
            unitBean.setGloCustomerCountry(Utility.resolveNull(rs.getString("GLO_CUSTOMER_COUNTRY")));
            unitBean.setTechnologyCode(rs.getString("TECHNOLOGY_CODE_OG"));                             //GIB Remediation Changes
            unitBean.setTechnologyDesc(Utility.resolveNull(rs.getString("TECHNOLOGY_DESC")));           
            unitBean.setTechnologyDescOg(Utility.resolveNull(rs.getString("TECHNOLOGY_DESC_OG")));
            unitBean.setEquipmentCode(Utility.resolveNull(rs.getString("EQUIPMENT_CODE")));
            unitBean.setEquipmentEngDesc(Utility.resolveNull(rs.getString("EQUIPMENT_ENG_DESC")));
            unitBean.setUnitCustomerName(Utility.resolveNull(rs.getString("UNIT_CUSTOMER_NAME")));
            unitBean.setEngProjectRef(Utility.resolveNull(rs.getString("ENG_PROJECT_REF")));
            unitBean.setOemLocationDesc(Utility.resolveNull(rs.getString("OEM_LOCATION_DESC")));

            unitBean.setUnitStatusDesc(Utility.resolveNull(rs.getString("UNIT_STATUS_DESC")));
            unitBean.setUnitShipDate(Utility.dateToString(rs.getDate("UNIT_SHIP_DATE")));
            unitBean.setUnitCodDate(Utility.dateToString(rs.getDate("UNIT_COD_DATE")));
            unitBean.setUnitRetireDate(Utility.dateToString(rs.getDate("UNIT_RETIRE_DATE")));
            unitBean.setServiceRelationCode(Utility.resolveNull(rs.getString("SERVICE_RELATION_CODE")));
            unitBean.setServiceRelationDesc(Utility.resolveNull(rs.getString("SERVICE_RELATION_DESC")));
            unitBean.setMainWarrantyActive(Utility.resolveNull(rs.getString("MAIN_WARRANTY_ACTIVE")));
            unitBean.setServiceWarrantyActive(Utility.resolveNull(rs.getString("SERVICE_WARRANTY_ACTIVE")));

            unitBean.setCsaEndDate(Utility.dateToString(rs.getDate("CSA_END_DATE")));
            unitBean.setOgSalesRegion(Utility.resolveNull(rs.getString("OG_SALES_REGION")));
            unitBean.setSanctionedUnitFlag(Utility.resolveNull(rs.getString("SANCTIONED_UNIT_FLAG")));
            unitBean.setUnitRating(Utility.resolveNull(rs.getString("UNIT_RATING")));
            unitBean.setUnitRatingUom(Utility.resolveNull(rs.getString("UNIT_RATING_UOM")));

            unitBean.setControlSystemDesc(Utility.resolveNull(rs.getString("CONTROL_SYSTEM_DESC")));

            unitBean.setServiceTypeDesc(Utility.resolveNull(rs.getString("SERVICE_TYPE_DESC")));

            unitBean.setDrivenEquipmentDesc(Utility.resolveNull(rs.getString("DRIVEN_EQUIPMENT_DESC")));

            unitBean.setCombustionSystemDesc(Utility.resolveNull(rs.getString("COMBUSTION_SYSTEM_DESC")));

            unitBean.setPrimaryFuelTypeDesc(Utility.resolveNull(rs.getString("PRIMARY_FUEL_TYPE_DESC")));
            unitBean.setExtendorKitInstalled(Utility.resolveNull(rs.getString("EXTENDOR_KIT_INSTALLED")));
            unitBean.setWhruFlag(Utility.resolveNull(rs.getString("WHRU_FLAG")));
            unitBean.setRmdServiceFlag(Utility.resolveNull(rs.getString("RMD_SERVICE_FLAG")));
            unitBean.setPilotServiceFlag(Utility.resolveNull(rs.getString("PILOT_SERVICE_FLAG")));
            unitBean.setLineupServiceDescription(Utility.resolveNull(rs.getString("LINEUP_SERVICE_DESC")));
            unitBean.setEquipmentLocationDescription(Utility.resolveNull(rs.getString("EQUIP_LOCATION_DESC")));
            unitBean.setLastUpdateDate(Utility.dateToString(rs.getDate("LAST_UPDATE_DATE")));
            unitBean.setComments(Utility.resolveNull(rs.getString("COMMENTS")));


            unitList.add(unitBean);
        
     catch (SQLException e) 
        throw new DialectException(e.getMessage());
    finally
        DBUtility.close(ps, rs);
        DBUtility.close(cs);
    


此外,当我使用ojdbc7.jar时,StructDescriptor类和ARRAY类显示为已弃用。有没有其他更好的方法来实现这一目标?任何帮助将不胜感激。提前致谢。

答案

弃用是为了阻止做整件事:使用Oracle Arrays和Structs,但无论如何它都会起作用。

简而言之,从Java类型到Oracle自定义类型的映射需要使用自定义Mybatis TypeHandler。类型处理程序中的代码基本上是JDBC。当涉及操纵数组和结构时,它强烈依赖于DB供应商的驱动程序API(根本不是标准的)。

设置Mybatis有很多内容,所以这里有特定的部分。

以下抽象类允许将java Array / Collection映射到Oracle Array / Table类型。只需在具体实现中指定Oracle端的类型名称:例如:List < - > TYPE ARRAY_INT AS TABLE OF NUMBER。

import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Locale;
import java.util.ResourceBundle;

import oracle.jdbc.OracleConnection;
import oracle.sql.ARRAY;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.log4j.Logger;
import org.jboss.jca.adapters.jdbc.WrappedConnection; // in case when the connection is managed by the container (jboss in this case)

public abstract class AbstractArrayTypeHandler<T> extends BaseTypeHandler<Object> 

    private static final Logger LOGGER = Logger.getLogger(AbstractArrayTypeHandler.class);

    protected static final ResourceBundle CONFIG   = ResourceBundle.getBundle("config", Locale.ENGLISH);

    protected static final String SCHEMA_NAME = CONFIG.getString("schema.name");

    protected static final ResourceBundle DB_STRUCTURE   = ResourceBundle.getBundle("dbStructure", Locale.ENGLISH);

    protected static final String TYPE_PACKAGE_NAME = DB_STRUCTURE.getString("type.package.name");

    protected abstract String getSqlType();

    @SuppressWarnings("rawtypes")
    @Override
    public void setNonNullParameter(final PreparedStatement stmt, final int index, final Object parameter,
            final JdbcType jdbcType) throws SQLException 
        Object[] javaArray;
        if (null == parameter) 
            throw new IllegalArgumentException("Parameter must not be null");
         else 
            if (parameter.getClass().isArray()) 
                javaArray = (Object[]) parameter;
             else if (parameter instanceof Collection) 
                javaArray = ((Collection) parameter).toArray();
             else 
                throw new IllegalArgumentException("Parameter must be array or collection");
            
            final Connection statementConnection = stmt.getConnection();
            Connection underlyingConnection = statementConnection;
            if (statementConnection instanceof WrappedConnection)  // unwrap the managed connection when necessary
                final WrappedConnection wrapper = (WrappedConnection) statementConnection;
                LOGGER.debug("Wrapped connection type: " + wrapper.getClass().getName());
                underlyingConnection = wrapper.getUnderlyingConnection();
            
            LOGGER.debug("Underlying connection type: " + underlyingConnection.getClass().getName());
            final OracleConnection oracleConnection = (OracleConnection) underlyingConnection;
            /* java.sqlConnection.createArrayOf is not supported by Oracle Driver */
            final String type = String.format("%s.%s.%s", SCHEMA_NAME, TYPE_PACKAGE_NAME, this.getSqlType());
            final Array array = createArray(oracleConnection, type, javaArray);
            LOGGER.debug(String.format("ARRAY type '%s' of %d elements created", type, javaArray.length));
            stmt.setArray(index, array);
            LOGGER.debug("statement array Set");
        
    

    protected ARRAY createArray(final OracleConnection oracleConnection, final String type, final Object[] javaArray) throws SQLException 
        return oracleConnection.createARRAY(type, javaArray);
    

    @Override
    public Object getNullableResult(final ResultSet resultSet, final String columnName) throws SQLException 
        LOGGER.debug("getNullableResult - resultSet/columnName");
        final Array array = resultSet.getArray(columnName);
        return array.getArray();
    

    @Override
    public Object getNullableResult(final ResultSet resultSet, final int columnIndex) throws SQLException 
        LOGGER.debug("getNullableResult - resultSet/columnIndex");
        final Array array = resultSet.getArray(columnIndex);
        return array.getArray();
    

    @Override
    public Object getNullableResult(final CallableStatement stmt, final int columnIndex) throws SQLException 
        LOGGER.debug("getNullableResult - callableStatement/columnIndex");
        final Array array = stmt.getArray(columnIndex);
        return array.getArray();
    


这里映射自定义Oracle类型/结构的数组:

import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.List;

import oracle.jdbc.OracleConnection;
import oracle.sql.ARRAY;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;

import org.apache.log4j.Logger;


@SuppressWarnings("deprecation")
public abstract class AbstractObjectArrayTypeHandler<T> extends AbstractArrayTypeHandler<T> 

    private static final Logger LOGGER = Logger.getLogger(ListbeanTypeHandler.class);

    public AbstractObjectArrayTypeHandler() 
        super();
    

    protected abstract String getOracleObjectType();

    protected abstract Class<T> arrayElementClass();

    protected abstract Object[] buildStructAttributes(Object object);

    @Override
    protected ARRAY createArray(OracleConnection oracleConnection, String oracleArrayType, Object[] javaArray) throws SQLException 
        StructDescriptor itemDescriptor = createDescriptor(oracleConnection);
        List<Struct> structList = new ArrayList<Struct>(javaArray.length);
        Class<T> arrayElementClass = arrayElementClass();
        for (Object object : javaArray) 
            if (null != object && arrayElementClass.isAssignableFrom(object.getClass())) 
                Object[] structAttributes = buildStructAttributes(object);
                structList.add(new STRUCT(itemDescriptor, oracleConnection, structAttributes));
             else throw new IllegalArgumentException("javaArray element must be instance of " + arrayElementClass.getName() + "but is: " + (null == object ? "null" : object.getClass().getName()));
        
        return super.createArray(oracleConnection, oracleArrayType, structList.toArray());
    

    private StructDescriptor createDescriptor(OracleConnection oracleConnection) throws SQLException 
        final String typeName = typeFullQualifiedName();
        StructDescriptor descriptor = StructDescriptor.createDescriptor(typeName, oracleConnection);
        LOGGER.debug(String.format("Object descriptor for type '%s' created", typeName));
        return descriptor;
    

    private String typeFullQualifiedName() 
        return String.format("%s.%s", SCHEMA_NAME, this.getOracleObjectType());
    

    @Override
    public Object getNullableResult(final ResultSet resultSet, final String columnName) throws SQLException 
        final Array array = resultSet.getArray(columnName);
        return readOracleStructList(array);
    

    @Override
    public Object getNullableResult(final ResultSet resultSet, final int columnIndex) throws SQLException 
        final Array array = resultSet.getArray(columnIndex);
        return readOracleStructList(array);
    

    @Override
    public Object getNullableResult(final CallableStatement stmt, final int columnIndex) throws SQLException 
        final Array array = stmt.getArray(columnIndex);
        return readOracleStructList(array);
    

    protected List<T> readOracleStructList(Array sqlArray) throws SQLException 
        if (null == sqlArray)
            return null;
        Object object = sqlArray.getArray();
        Object[] structObjectArray;
        return null == object ? null : readNotNullStructList(obj

以上是关于JDBC 调用自定义函数(常说的存储过程)的步骤的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis - 使用自定义对象调用存储过程

数据库小技能:存储过程(stored procedure)

MySQL存储过程和自定义函数Navicat for mysql创建存储过程和函数调用存储过程和函数的区别

JDBC 调用函数或存储过程

Java数据库连接——JDBC调用存储过程,事务管理和高级应用

如何从 jdbc 调用存储的函数?