杰瑞科技汇

Oracle Java存储过程如何调用与调试?

Java 存储过程

Java 存储过程,也称为 Java 存储函数/过程,是指将 Java 类的方法发布为 Oracle 数据库中的一个存储过程、函数或包,数据库本身充当了一个 Java 运行时环境,可以编译并执行你的 Java 代码。

Oracle Java存储过程如何调用与调试?-图1
(图片来源网络,侵删)

主要用途

  1. 复用现有 Java 代码:如果你的业务逻辑已经用 Java 实现,无需重写,可以直接部署到数据库中。
  2. 访问外部资源:Java 代码可以访问文件系统、网络(HTTP 请求、调用 Web Service)、发送邮件等,这是纯 PL/SQL 难以做到的。
  3. 复杂计算和算法:对于科学计算、图像处理、加密解密等复杂算法,使用 Java 通常比 PL/SQL 更高效和方便。
  4. 集成第三方库:可以轻松引入各种成熟的 Java 开源库(如 Apache Commons, Log4j 等)。
  5. 提高性能:对于某些计算密集型任务,在数据库内部执行 Java 可以避免数据在应用服务器和数据库之间的网络传输。

实现步骤

创建一个 Java 存储过程通常遵循以下四个步骤:

  1. 编写 Java 源代码
  2. 编译 Java 源代码
  3. 将 Java 类加载到 Oracle 数据库
  4. 在数据库中创建调用该 Java 方法的 PL/SQL 封装器(Wrapper)

详细示例:创建一个简单的 Java 存储过程

我们将创建一个 Java 方法,它接收一个字符串,然后返回该字符串的 MD5 哈希值,由于 PL/SQL 本身没有内置的 MD5 函数,这是一个非常典型的使用场景。

步骤 1: 编写 Java 源代码

创建一个 Java 类,这个类必须是 public 的,并且你希望调用的方法也必须是 publicstatic 的。

Md5Generator.java

Oracle Java存储过程如何调用与调试?-图2
(图片来源网络,侵删)
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Generator {
    /**
     * 计算字符串的 MD5 哈希值
     * @param input 输入字符串
     * @return MD5 哈希值的十六进制字符串
     * @throws NoSuchAlgorithmException
     */
    public static String generateMd5(String input) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] messageDigest = md.digest(input.getBytes());
        // 将字节数组转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : messageDigest) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

代码要点:

  • public class Md5Generator: 定义一个公共类。
  • public static String generateMd5(String input): 定义一个公共静态方法,这是我们将在 Oracle 中调用的方法。
  • throws NoSuchAlgorithmException: 方法声明可能抛出的异常,PL/SQL 可以捕获这个异常。

步骤 2: 编译 Java 源代码

你需要使用 JDK 的 javac 编译器来编译这个 .java 文件。

# 确保你的环境变量中配置了 JDK
javac Md5Generator.java

编译成功后,会生成 Md5Generator.class 文件。

步骤 3: 将 Java 类加载到 Oracle 数据库

这一步是关键,你需要使用 Oracle 的 loadjava 实用工具将编译好的 .class 文件上传到数据库中。

Oracle Java存储过程如何调用与调试?-图3
(图片来源网络,侵删)

loadjava 命令格式:

loadjava -user <username>/<password>@<database_connection_string> -resolve <path_to_class_file>
  • -user: 你的数据库用户名、密码和连接字符串。
  • -resolve: 这是一个重要选项,它不仅加载类,还会立即编译并解析 Java 类的依赖项,确保其可以正常执行。强烈建议始终使用 -resolve

示例:

loadjava -user scott/tiger@localhost:1521/XE -resolve Md5Generator.class

执行后,你可以查询 USER_OBJECTS 视图来确认 Java 类是否已成功加载。

SELECT object_name, object_type, status
FROM user_objects
WHERE object_name = 'MD5GENERATOR';

你应该能看到类似下面的结果:

OBJECT_NAME         OBJECT_TYPE         STATUS
-------------------- -------------------- --------
MD5GENERATOR         JAVA CLASS          VALID

步骤 4: 在数据库中创建 PL/SQL 封装器

我们创建一个 PL/SQL 存储过程或函数,作为 Java 方法的“代理”,这个代理负责在 Java 和 PL/SQL 世界之间进行数据类型转换。

我们将创建一个函数,因为 Java 方法返回一个值。

create_md5_function.sql

CREATE OR REPLACE FUNCTION get_md5_hash(p_input_string IN VARCHAR2)
RETURN VARCHAR2
AS
  -- 声明一个外部引用,指向我们加载的 Java 类和方法
  LANGUAGE JAVA
  NAME 'Md5Generator.generateMd5(java.lang.String) return java.lang.String';
/

PL/SQL 封装器语法解析:

  • CREATE OR REPLACE FUNCTION get_md5_hash(...): 创建一个名为 get_md5_hash 的函数。
  • p_input_string IN VARCHAR2: 定义一个名为 p_input_string 的输入参数,类型为 VARCHAR2
  • RETURN VARCHAR2: 定义函数返回值的类型为 VARCHAR2
  • AS: PL/SQL 代码块开始。
  • LANGUAGE JAVA: 指明这是一个外部语言调用。
  • NAME '...': 这是核心部分,它定义了 Java 方法的完整签名。
    • 'Md5Generator.generateMd5(java.lang.String) return java.lang.String'
    • Md5Generator: Java 类名。
    • .generateMd5: Java 方法名。
    • (java.lang.String): Java 方法的参数类型。VARCHAR2 在 PL/SQL 中会自动映射到 java.lang.String
    • return java.lang.String: Java 方法的返回类型。VARCHAR2 会自动映射到 java.lang.String

调用 Java 存储过程

你可以像调用任何普通的 PL/SQL 函数一样调用它了。

-- 调用函数
SELECT get_md5_hash('Hello, Oracle!') AS md5_hash FROM dual;

预期输出:

MD5_HASH
----------------------------------
6dd1379013f964b6a9d5e855f9a4b9c3

更复杂的示例:带有 IN OUT 参数和异常处理

让我们再看一个例子,演示如何处理 IN OUT 参数和 Java 抛出的异常。

Java 代码

StringFormatter.java

public class StringFormatter {
    public static String formatText(String text, int maxLength, String suffix) {
        if (text == null) {
            throw new IllegalArgumentException("Input text cannot be null");
        }
        if (suffix == null) {
            suffix = "...";
        }
        if (text.length() <= maxLength) {
            return text;
        }
        return text.substring(0, maxLength) + suffix;
    }
}

加载到数据库

loadjava -user scott/tiger@localhost:1521/XE -resolve StringFormatter.class

创建 PL/SQL 封装器(过程)

这次我们创建一个 PROCEDURE,因为它会修改一个传入的字符串。

CREATE OR REPLACE PROCEDURE format_string_proc (
    p_text_in IN VARCHAR2,
    p_max_len IN NUMBER,
    p_suffix IN VARCHAR2,
    p_text_out OUT VARCHAR2
)
AS
  LANGUAGE JAVA
  NAME 'StringFormatter.formatText(java.lang.String, int, java.lang.String) return java.lang.String';
/

注意,PL/SQL 的 NUMBER 类型会映射到 Java 的 intjava.math.BigDecimal,对于简单整数,int 即可。

调用并处理异常

DECLARE
  v_original_text CLOB := 'This is a very long string that needs to be truncated for display purposes.';
  v_formatted_text VARCHAR2(4000);
BEGIN
  -- 正常调用
  format_string_proc(v_original_text, 20, '...', v_formatted_text);
  DBMS_OUTPUT.PUT_LINE('Formatted: ' || v_formatted_text);
  -- 演示异常处理
  BEGIN
    format_string_proc(NULL, 10, '...', v_formatted_text);
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('Caught an exception: ' || SQLERRM);
  END;
END;
/

预期输出:

Formatted: This is a very long...
Caught an exception: java.lang.IllegalArgumentException: Input text cannot be null

数据类型映射表

在编写 PL/SQL 封装器时,了解 PL/SQL 和 Java 之间的数据类型映射非常重要。

PL/SQL 类型 Java 类型 说明
VARCHAR2, CHAR java.lang.String 最常用的映射。
NUMBER java.math.BigDecimal 精确的十进制数。
NUMBER(p, s) java.math.BigDecimaldouble p 是精度,s 是小数位数。
BINARY_INTEGER, PLS_INTEGER int 整数。
DATE java.sql.Date 日期部分。
TIMESTAMP java.sql.Timestamp 日期和时间。
BLOB oracle.sql.BLOB 二进制大对象。
CLOB oracle.sql.CLOB 字符大对象。
BFILE oracle.sql.BFILE 外部二进制文件。
RECORD, %ROWTYPE` N/A 不能直接映射,需要创建一个 Object Type 来对应 Java 的类。

重要注意事项

  1. 权限:执行 loadjava 的用户需要有 JAVAUSERPRIVILEGE 权限,要创建外部过程,用户还需要 CREATE PROCEDURE 权限。
  2. 性能:对于非常简单的逻辑,直接使用 PL/SQL 会更快,因为避免了 Java 解释的开销,Java 存储过程最适合处理复杂逻辑和外部交互。
  3. 状态管理:Java 代码在数据库中是无状态的,每次调用都是独立的,不要依赖静态变量来保存会话状态。
  4. 调试:调试 Java 存储过程比调试 PL/SQL 复杂,通常需要查看数据库的 Java 日志或使用专门的调试工具。
  5. 安全性:在数据库中执行 Java 代码会带来安全风险,确保你加载的 Java 代码来源可靠,并遵循最小权限原则。

通过以上步骤和示例,你应该能够掌握在 Oracle 数据库中创建和使用 Java 存储过程的基本方法,这是一种非常强大的技术,可以极大地扩展 Oracle 数据库的能力边界。

分享:
扫描分享到社交APP
上一篇
下一篇