Java 中文官方教程 2022 版(三十六)(4)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: Java 中文官方教程 2022 版(三十六)

Java 中文官方教程 2022 版(三十六)(3)https://developer.aliyun.com/article/1488074

使用 SQL 脚本或 JDBC API 在 Java DB 中创建存储过程

Java DB 使用 Java 编程语言进行存储过程。因此,当您定义存储过程时,您需要指定要调用的 Java 类以及 Java DB 可以找到它的位置。

以下摘录自StoredProcedureJavaDBSample.createProcedures创建了一个名为SHOW_SUPPLIERS的存储过程:

public void createProcedures(Connection con)
    throws SQLException {
    Statement stmtCreateShowSuppliers = null;
    // ...
    String queryShowSuppliers =
        "CREATE PROCEDURE SHOW_SUPPLIERS() " +
        "PARAMETER STYLE JAVA " +
        "LANGUAGE JAVA " +
        "DYNAMIC RESULT SETS 1 " +
        "EXTERNAL NAME " +
        "'com.oracle.tutorial.jdbc." +
        "StoredProcedureJavaDBSample." +
        "showSuppliers'";
    // ...
    try {
        System.out.println("Calling CREATE PROCEDURE");
        stmtCreateShowSuppliers = con.createStatement();
        // ...
    } catch (SQLException e) {
        JDBCTutorialUtilities.printSQLException(e);
    } finally {
        if (stmtCreateShowSuppliers != null) {
            stmtCreateShowSuppliers.close();
        }
        // ...
    }
}

以下列表描述了您可以在CREATE PROCEDURE语句中指定的过程元素:

  • PARAMETER STYLE:标识用于将参数传递给存储过程的约定。以下选项有效:
  • JAVA:指定存储过程使用符合 Java 语言和 SQL 例程规范的参数传递约定。
  • DERBY:指定存储过程支持参数列表中的最后一个参数作为可变参数。
  • LANGUAGE JAVA:指定存储过程的编程语言(目前,JAVA是唯一的选项)。
  • DYNAMIC RESULT SETS 1:指定检索的最大结果集数量;在本例中为1
  • EXTERNAL NAME 'com.oracle.tutorial.jdbc.StoredProcedureJavaDBSample.showSuppliers' 指定了此存储过程调用的完全限定的 Java 方法。注意:Java DB 必须能够在类路径或直接添加到数据库的 JAR 文件中找到此处指定的方法。请参阅以下步骤,将 Java 类打包到 JAR 文件中。

以下语句(位于 StoredProcedureJavaDBSample.createProcedures 中)创建了一个名为 GET_SUPPLIERS_OF_COFFEE 的存储过程(为了清晰起见添加了换行符):

CREATE PROCEDURE GET_SUPPLIER_OF_COFFEE(
    IN coffeeName varchar(32),
    OUT supplierName
    varchar(40))
    PARAMETER STYLE JAVA
    LANGUAGE JAVA
    DYNAMIC RESULT SETS 0
    EXTERNAL NAME 'com.oracle.tutorial.jdbc.
        StoredProcedureJavaDBSample.
        getSupplierOfCoffee'

此存储过程有两个形式参数,coffeeNamesupplierName。参数说明符 INOUT 被称为参数模式。它们定义了形式参数的操作。有关更多信息,请参阅参数模式。此存储过程不检索结果集,因此过程元素 DYNAMIC RESULT SETS0

以下语句创建了一个名为 RAISE_PRICE 的存储过程(为了清晰起见添加了换行符):

CREATE PROCEDURE RAISE_PRICE(
    IN coffeeName varchar(32),
    IN maximumPercentage float,
    INOUT newPrice float)
    PARAMETER STYLE JAVA
    LANGUAGE JAVA
    DYNAMIC RESULT SETS 0
    EXTERNAL NAME 'com.oracle.tutorial.jdbc.
        StoredProcedureJavaDBSample.raisePrice'

您可以使用 SQL 脚本在 Java DB 中创建存储过程。查看脚本 javadb/create-procedures.sqlbuild.xml Ant 构建脚本中的 Ant 目标 javadb-create-procedure

在 Java DB 中调用存储过程

以下摘录自方法StoredProcedureJavaDBSample.runStoredProcedures 调用存储过程 SHOW_SUPPLIERS 并打印生成的结果集:

cs = this.con.prepareCall("{call SHOW_SUPPLIERS()}");
ResultSet rs = cs.executeQuery();
while (rs.next()) {
    String supplier = rs.getString("SUP_NAME");
    String coffee = rs.getString("COF_NAME");
    System.out.println(supplier + ": " + coffee);
}

注意:与 Statement 对象一样,要调用存储过程,可以根据过程返回多少个 ResultSet 对象来调用 executeexecuteQueryexecuteUpdate。但是,如果不确定过程返回多少个 ResultSet 对象,请调用 execute

以下摘录自方法 StoredProcedureJavaDBSample.runStoredProcedures 调用存储过程 GET_SUPPLIER_OF_COFFEE

cs = this.con.prepareCall("{call GET_SUPPLIER_OF_COFFEE(?, ?)}");
cs.setString(1, coffeeNameArg);
cs.registerOutParameter(2, Types.VARCHAR);
cs.executeQuery();
String supplierName = cs.getString(2);

接口 CallableStatement 扩展了 PreparedStatement。它用于调用存储过程。像使用 PreparedStatement 对象一样,通过调用适当的 setter 方法为 IN 参数(例如本例中的 coffeeName)指定值。但是,如果存储过程包含 OUT 参数,则必须使用 registerOutParameter 方法进行注册。

以下摘录自方法 StoredProcedureJavaDBSample.runStoredProcedures 调用存储过程 RAISE_PRICE

cs = this.con.prepareCall("{call RAISE_PRICE(?,?,?)}");
cs.setString(1, coffeeNameArg);
cs.setFloat(2, maximumPercentageArg);
cs.registerOutParameter(3, Types.NUMERIC);
cs.setFloat(3, newPriceArg);
cs.execute();

因为参数 newPrice(过程 RAISE_PRICE 中的第三个参数)具有参数模式 INOUT,您必须通过调用适当的 setter 方法指定其值,并使用 registerOutParameter 方法进行注册。

将 Java 类打包到 JAR 文件中

Ant 构建脚本 build.xml 包含编译和打包教程为 JAR 文件的目标。在命令提示符下,将当前目录更改为 **。从该目录运行以下命令编译并打包教程为 JAR 文件:

ant jar

JAR 文件的名称是 **/lib/JDBCTutorial.jar

Ant 构建脚本将文件 JDBCTutorial.jar 添加到类路径中。您还可以在 CLASSPATH 环境变量中指定 JAR 文件的位置。这样可以使 Java DB 找到存储过程调用的 Java 方法。

直接向数据库添加 JAR 文件

Java DB 首先在类路径中查找所需的类,然后在数据库中查找。本节展示了如何直接向数据库添加 JAR 文件。

使用以下系统存储过程将 JDBCTutorial.jar JAR 文件添加到数据库中(为了清晰起见已添加换行符):

CALL sqlj.install_jar(
  '*<JDBC tutorial directory>*/
  lib/JDBCTutorial.jar',
  'APP.JDBCTutorial', 0)
CALL sqlj.replace_jar(
  '*<JDBC tutorial directory>*/
  lib/JDBCTutorial.jar',
  'APP.JDBCTutorial')";
CALL syscs_util.syscs_set_database_property(
  'derby.database.classpath',
  'APP.JDBCTutorial')";

注意:方法 StoredProcedureJavaDBSample.registerJarFile 演示了如何调用这些系统存储过程。如果调用此方法,请确保已修改 javadb-sample-properties.xml,使属性 jar_file 的值设置为 JDBCTutorial.jar 的完整路径名。

SQL 模式中的 install_jar 过程向数据库添加 JAR 文件。此过程的第一个参数是在运行此过程的计算机上 JAR 文件的完整路径名。第二个参数是 Java DB 用于引用 JAR 文件的标识符。(标识符 APP 是 Java DB 默认模式。)replace_jar 过程替换数据库中已有的 JAR 文件。

系统存储过程 SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY 在当前连接上设置或删除数据库属性的值。此方法将属性 derby.database.classpath 设置为 install_jar 文件中指定的标识符。Java DB 首先在 Java 类路径中查找类,然后查找 derby.database.classpath

在 MySQL 中创建存储过程

在 Java DB 中创建和使用存储过程涉及以下步骤:

  1. 使用 SQL 脚本或 JDBC API 创建存储过程。
  2. 使用 CALL SQL 语句调用存储过程。参见 在 MySQL 中调用存储过程 部分。

使用 SQL 脚本或 JDBC API 在 MySQL 中创建存储过程

MySQL 使用基于 SQL 的语法来编写存储过程。以下摘录来自 SQL 脚本 mysql/create-procedures.sql 创建了名为 SHOW_SUPPLIERS 的存储过程:

SELECT 'Dropping procedure SHOW_SUPPLIERS' AS ' '|
drop procedure if exists SHOW_SUPPLIERS|
# ...
SELECT 'Creating procedure SHOW_SUPPLIERS' AS ' '|
create procedure SHOW_SUPPLIERS()
    begin
        select SUPPLIERS.SUP_NAME,
        COFFEES.COF_NAME
        from SUPPLIERS, COFFEES
        where SUPPLIERS.SUP_ID = COFFEES.SUP_ID
        order by SUP_NAME;
    end|

DROP PROCEDURE 语句会删除存储过程 SHOW_SUPPLIERS(如果存在的话)。在 MySQL 中,存储过程中的语句用分号分隔。然而,结束 create procedure 语句需要一个不同的分隔符。这个示例使用了竖线(|)字符;你可以使用其他字符(或多个字符)。分隔语句的字符在调用这个脚本的 Ant 目标中的 delimiter 属性中定义。这段摘录来自 Ant 构建文件 build.xml(为了清晰起见插入了换行符):

<target name="mysql-create-procedure">
  <sql driver="${DB.DRIVER}"
       url="${DB.URL}" userid="${DB.USER}"
       password="${DB.PASSWORD}"
       classpathref="CLASSPATH"
       print="true"
       delimiter="|"
       autocommit="false"
       onerror="abort">
       <transaction
         src="./sql/${DB.VENDOR}/
           create-procedures.sql">
       </transaction>
  </sql>
</target>

或者,你可以使用 DELIMITER SQL 语句来指定一个不同的分隔符字符。

CREATE PROCEDURE 语句由过程的名称、括号中以逗号分隔的参数列表以及 BEGINEND 关键字内的 SQL 语句组成。

你可以使用 JDBC API 来创建存储过程。下面的方法 StoredProcedureMySQLSample.createProcedureShowSuppliers 执行了与前面脚本相同的任务:

public void createProcedureShowSuppliers() throws SQLException {
    String queryDrop = "DROP PROCEDURE IF EXISTS SHOW_SUPPLIERS";
    String createProcedure =
        "create procedure SHOW_SUPPLIERS() " +
          "begin " +
            "select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME " +
              "from SUPPLIERS, COFFEES " +
              "where SUPPLIERS.SUP_ID = COFFEES.SUP_ID " +
              "order by SUP_NAME; " +
          "end";
    try (Statement stmtDrop = con.createStatement()) {
      System.out.println("Calling DROP PROCEDURE");
      stmtDrop.execute(queryDrop);
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    } 
    try (Statement stmt = con.createStatement()) {
      stmt.executeUpdate(createProcedure);
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    }
  }

请注意,在这个方法中分隔符没有被改变。

存储过程 SHOW_SUPPLIERS 生成一个结果集,尽管方法 createProcedureShowSuppliers 的返回类型是 void,并且该方法不包含任何参数。当使用方法 CallableStatement.executeQuery 调用存储过程 SHOW_SUPPLIERS 时,会返回一个结果集:

CallableStatement cs = null;
cs = this.con.prepareCall("{call SHOW_SUPPLIERS}");
ResultSet rs = cs.executeQuery();

下面从方法 StoredProcedureMySQLSample.createProcedureGetSupplierOfCoffee 中提取的内容包含了创建名为 GET_SUPPLIER_OF_COFFEE 的存储过程的 SQL 查询:

public void createProcedureGetSupplierOfCoffee() throws SQLException {
    String queryDrop = "DROP PROCEDURE IF EXISTS GET_SUPPLIER_OF_COFFEE";
    String createProcedure =
        "create procedure GET_SUPPLIER_OF_COFFEE(IN coffeeName varchar(32), OUT supplierName varchar(40)) " +
          "begin " +
            "select SUPPLIERS.SUP_NAME into supplierName " +
              "from SUPPLIERS, COFFEES " +
              "where SUPPLIERS.SUP_ID = COFFEES.SUP_ID " +
              "and coffeeName = COFFEES.COF_NAME; " +
            "select supplierName; " +
          "end";
    try (Statement stmtDrop = con.createStatement()) {
      System.out.println("Calling DROP PROCEDURE");
      stmtDrop.execute(queryDrop);
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    }
    try (Statement stmt = con.createStatement()) {
      stmt.executeUpdate(createProcedure);
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    }
  }

这个存储过程有两个形式参数,coffeeNamesupplierName。参数说明符 INOUT 被称为参数模式。它们定义了形式参数的作用。更多信息请参见 参数模式。形式参数在 SQL 查询中定义,而不是在方法 createProcedureGetSupplierOfCoffee 中。为了给 OUT 参数 supplierName 赋值,这个存储过程使用了一个 SELECT 语句。

下面从方法 StoredProcedureMySQLSample.createProcedureRaisePrice 中提取的内容包含了创建名为 RAISE_PRICE 的存储过程的 SQL 查询:

public void createProcedureRaisePrice() throws SQLException {
    String queryDrop = "DROP PROCEDURE IF EXISTS RAISE_PRICE";
    String createProcedure =
        "create procedure RAISE_PRICE(IN coffeeName varchar(32), IN maximumPercentage float, INOUT newPrice numeric(10,2)) " +
          "begin " +
            "main: BEGIN " +
              "declare maximumNewPrice numeric(10,2); " +
              "declare oldPrice numeric(10,2); " +
              "select COFFEES.PRICE into oldPrice " +
                "from COFFEES " +
                "where COFFEES.COF_NAME = coffeeName; " +
              "set maximumNewPrice = oldPrice * (1 + maximumPercentage); " +
              "if (newPrice > maximumNewPrice) " +
                "then set newPrice = maximumNewPrice; " +
              "end if; " +
              "if (newPrice <= oldPrice) " +
                "then set newPrice = oldPrice;" +
                "leave main; " +
              "end if; " +
              "update COFFEES " +
                "set COFFEES.PRICE = newPrice " +
                "where COFFEES.COF_NAME = coffeeName; " +
              "select newPrice; " +
            "END main; " +
          "end";
    try (Statement stmtDrop = con.createStatement()) {
      System.out.println("Calling DROP PROCEDURE");
      stmtDrop.execute(queryDrop);
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    }
    try (Statement stmt = con.createStatement()) {
      stmt.executeUpdate(createProcedure);
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
    }
  }

存储过程使用 SETSELECT 语句给 INOUT 参数 newPrice 赋值。为了退出存储过程,存储过程首先将语句封装在一个标记为 mainBEGIN ... END 块中。为了退出过程,方法使用语句 leave main

在 MySQL 中调用存储过程

在 MySQL 中调用存储过程与在 Java DB 中调用它们相同。

下面是从方法StoredProcedureMySQLSample.runStoredProcedures中调用存储过程SHOW_SUPPLIERS并打印生成的结果集:

cs = this.con.prepareCall("{call SHOW_SUPPLIERS}");
      ResultSet rs = cs.executeQuery();
      while (rs.next()) {
        String supplier = rs.getString("SUP_NAME");
        String coffee = rs.getString("COF_NAME");
        System.out.println(supplier + ": " + coffee);
      }

注意:与Statement对象一样,要调用存储过程,可以根据过程返回的ResultSet对象数量调用executeexecuteQueryexecuteUpdate。但是,如果不确定过程返回多少个ResultSet对象,请调用execute

下面是从方法StoredProcedureMySQLSample.runStoredProcedures中调用存储过程GET_SUPPLIER_OF_COFFEE的摘录:

cs = this.con.prepareCall("{call GET_SUPPLIER_OF_COFFEE(?, ?)}");
      cs.setString(1, coffeeNameArg);
      cs.registerOutParameter(2, Types.VARCHAR);
      cs.executeQuery();
      String supplierName = cs.getString(2);

接口CallableStatement扩展了PreparedStatement。它用于调用存储过程。像使用PreparedStatement对象一样,通过调用适当的 setter 方法为IN参数(例如本例中的coffeeName)指定值。但是,如果存储过程包含OUT参数,必须使用registerOutParameter方法注册它。

下面是从方法StoredProcedureMySQLSample.runStoredProcedures中调用存储过程RAISE_PRICE的摘录:

cs = this.con.prepareCall("{call RAISE_PRICE(?,?,?)}");
      cs.setString(1, coffeeNameArg);
      cs.setFloat(2, maximumPercentageArg);
      cs.registerOutParameter(3, Types.NUMERIC);
      cs.setFloat(3, newPriceArg);
      cs.execute();

因为参数newPrice(过程RAISE_PRICE中的第三个参数)具有参数模式INOUT,您必须通过调用适当的 setter 方法指定其值,并使用registerOutParameter方法注册它。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
7月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
360 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
3月前
|
Oracle Java 关系型数据库
java 编程基础入门级超级完整版教程详解
这份文档是针对Java编程入门学习者的超级完整版教程,涵盖了从环境搭建到实际项目应用的全方位内容。首先介绍了Java的基本概念与开发环境配置方法,随后深入讲解了基础语法、控制流程、面向对象编程的核心思想,并配以具体代码示例。接着探讨了常用类库与API的应用,如字符串操作、集合框架及文件处理等。最后通过一个学生成绩管理系统的实例,帮助读者将理论知识应用于实践。此外,还提供了进阶学习建议,引导学员逐步掌握更复杂的Java技术。适合初学者系统性学习Java编程。资源地址:[点击访问](https://pan.quark.cn/s/14fcf913bae6)。
305 2
|
8月前
|
消息中间件 Java 数据库
自研Java框架 Sunrays-Framework使用教程「博客之星」
### Sunrays-Framework:助力高效开发的Java微服务框架 **Sunrays-Framework** 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件。它旨在简化数据访问、缓存管理、消息队列、文件存储等常见开发任务,帮助开发者快速构建高质量的企业级应用。 #### 核心功能 - **MyBatis-Plus**:简化数据访问层开发,提供强大的 CRUD 操作和分页功能。 - **Redis**:实现高性能缓存和分布式锁,提升系统响应速度。 - **RabbitMQ**:可靠的消息队列支持,适用于异步
自研Java框架 Sunrays-Framework使用教程「博客之星」
|
9月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
8405 5
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
8月前
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
133 4
|
8月前
|
存储 移动开发 算法
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
145 1
|
9月前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
556 26
|
8月前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
415 0
|
9月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
9月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)

热门文章

最新文章