- 编写 Java 代码:创建一个 Java 类,其中包含一个或多个
public static方法。 - 将 Java 代码加载到 Oracle 数据库:使用
loadjava命令或 SQL*Plus 将编译后的.class文件上传到数据库中,并将其创建为模式对象(JAVA SOURCE,JAVA CLASS,JAVA RESOURCE)。 - 在 Oracle 中创建 PL/SQL 封装:创建一个
PROCEDURE或FUNCTION声明,它作为 PL/SQL 和 Java 代码之间的桥梁,这个声明会告诉 Oracle 如何调用你的 Java 方法。
下面我们通过一个完整的、分步的示例来详细说明这个过程。

示例:创建一个 Java 存储过程,用于计算字符串的 MD5 哈希值
MD5 算法在标准 Java 中有实现,但在 PL/SQL 中没有内置函数,因此这是一个很好的用例。
第 1 步:编写 Java 代码
我们创建一个 Java 类,它包含一个 public static 方法来计算 MD5 哈希值。
MD5Utils.java
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Utils {
/**
* 计算字符串的 MD5 哈希值,并以十六进制字符串形式返回。
* 这个方法必须是 public static 的,才能被 Oracle PL/SQL 调用。
*
* @param inputStr 要哈希的输入字符串
* @return MD5 哈希值的十六进制字符串
* @throws NoSuchAlgorithmException 如果找不到 MD5 算法
*/
public static String getMD5(String inputStr) throws NoSuchAlgorithmException {
// MessageDigest 是线程不安全的,每次调用都创建新实例是安全的做法
MessageDigest md = MessageDigest.getInstance("MD5");
// 计算 MD5 哈希,返回一个字节数组
byte[] messageDigest = md.digest(inputStr.getBytes());
// 将字节数组转换为符号整型
BigInteger no = new BigInteger(1, messageDigest);
// 将整型转换为十六进制字符串
String hashtext = no.toString(16);
// 为了保证 32 位,前面补零
while (hashtext.length() < 32) {
hashtext = "0" + hashtext;
}
return hashtext;
}
}
关键点:

public static:Oracle 只能调用public和static的方法,实例方法(非静态)无法被直接调用。- 异常处理:Java 方法可以抛出异常,在 PL/SQL 中调用时,通常使用
EXCEPTIONS块来捕获和处理这些异常。 - 依赖:此代码仅使用 Java 标准库,无需额外 JAR 包。
第 2 步:编译 Java 代码并加载到 Oracle 数据库
你需要先将 .java 文件编译成 .class 文件,然后将其加载到数据库中。
使用命令行工具 loadjava (推荐)
-
编译 Java 文件:
javac MD5Utils.java
这会生成
MD5Utils.class文件。 -
使用
loadjava上传: 假设你的数据库用户是scott,密码是tiger,数据库服务名是orcl。loadjava -u scott/tiger@orcl -v -r MD5Utils.class
-u scott/tiger@orcl:指定数据库连接。-v:详细模式,可以看到加载过程的详细信息。-r:以RESOUCE形式加载,这是最常用的方式,会创建JAVA SOURCE,JAVA CLASS,JAVA RESOURCE三种对象。
*使用 SQLPlus / SQL Developer**
-
获取
.class文件的 Base64 编码: 你需要一个工具(如 Python 脚本或在线工具)将MD5Utils.class文件转换为 Base64 字符串,这通常比较繁琐,loadjava是首选。 -
*在 SQLPlus 中执行
LOADJAVA命令**:-- 连接到数据库 -- CONNECT scott/tiger@orcl -- 执行 loadjava 命令 -- 注意:你需要提供完整的路径到 .class 文件 -- HOST loadjava -u scott/tiger@orcl -v -r /path/to/your/MD5Utils.class
加载成功后,你可以查询 USER_OBJECTS 视图来确认:
SELECT object_name, object_type, status FROM user_objects WHERE object_name = 'MD5UTILS';
你应该能看到 JAVA CLASS, JAVA SOURCE 等类型的对象。
第 3 步:创建 PL/SQL 封装过程
我们创建一个 PL/SQL 过程,它作为 Java 方法的“代理”。
CREATE OR REPLACE PROCEDURE
CREATE OR REPLACE PROCEDURE compute_md5_hash (
p_input_string IN VARCHAR2,
p_md5_hash OUT VARCHAR2,
p_error_msg OUT VARCHAR2
)
AS
LANGUAGE JAVA
NAME 'MD5Utils.getMD5(java.lang.String) return java.lang.String';
BEGIN
-- 尝试调用 Java 方法
-- Java 方法抛出异常,控制权会自动跳转到下面的 EXCEPTIONS 块
p_md5_hash := MD5_UTILS_PKG.get_md5_hash(p_input_string);
-- 如果执行到这里,说明成功
p_error_msg := NULL;
EXCEPTION
WHEN OTHERS THEN
-- 捕获所有在 Java 中抛出的异常
p_error_msg := 'Java Error: ' || SQLERRM;
p_md5_hash := NULL;
END compute_md5_hash;
/
代码解释:
CREATE OR REPLACE PROCEDURE ...:标准的 PL/SQL 过程创建语法。p_input_string IN VARCHAR2:输入参数,类型是VARCHAR2,Oracle 会自动将其转换为 Java 的java.lang.String。p_md5_hash OUT VARCHAR2:输出参数,用于接收 Java 方法的返回值,Java 的String会自动转换为 PL/SQL 的VARCHAR2。p_error_msg OUT VARCHAR2:自定义的输出参数,用于传递错误信息。AS LANGUAGE JAVA ...:这是关键部分,它告诉 Oracle 这是一个 Java 调用。NAME '...':指定要调用的 Java 方法的完整签名,格式为'类名.方法名(参数类型) 返回类型'。'MD5Utils.getMD5(java.lang.String) return java.lang.String':这表示调用MD5Utils类中的getMD5方法,它接受一个java.lang.String参数,并返回一个java.lang.String。
EXCEPTION WHEN OTHERS THEN:这是处理 Java 异常的标准方式,Java 方法抛出NoSuchAlgorithmException或任何其他异常,PL/SQL 会捕获它,并将错误信息(SQLERRM)存入p_error_msg参数。
注意:上面的代码中,我直接调用了 MD5_UTILS_PKG.get_md5_hash(p_input_string),这不对,应该是直接赋值:
-- 正确的调用方式 p_md5_hash := MD5Utils.getMD5(p_input_string); -- 错误,PL/SQL不能直接这样调用 -- 正确的方式是通过一个 PL/SQL 函数或过程来封装,如下面这个函数版本。
让我们创建一个更常用的函数版本,这样在 SQL 语句中也可以使用:
CREATE OR REPLACE FUNCTION
CREATE OR REPLACE FUNCTION get_md5_hash_func (
p_input_string IN VARCHAR2
) RETURN VARCHAR2
AS
LANGUAGE JAVA
NAME 'MD5Utils.getMD5(java.lang.String) return java.lang.String';
/
这个函数版本更简洁,因为它只有一个输入参数和一个返回值,它不便于处理异常,Java 方法抛出异常,整个函数调用会失败。
最佳实践:创建一个包来封装 为了更好地组织代码和处理异常,推荐使用包。
MD5_UTILS_PKG.sql
CREATE OR REPLACE PACKAGE MD5_UTILS_PKG AS
-- 公开的函数,用于在 SQL 和 PL/SQL 中调用
FUNCTION get_md5_hash(p_input_string IN VARCHAR2) RETURN VARCHAR2;
-- 公开的存储过程,带有详细的错误输出
PROCEDURE compute_md5_hash(
p_input_string IN VARCHAR2,
p_md5_hash OUT VARCHAR2,
p_error_msg OUT VARCHAR2
);
END MD5_UTILS_PKG;
/
CREATE OR REPLACE PACKAGE BODY MD5_UTILS_PKG AS
-- 内部函数,实际调用 Java
-- 注意:函数名不要和包名冲突
FUNCTION call_java_md5(p_input_string IN VARCHAR2) RETURN VARCHAR2
AS
LANGUAGE JAVA
NAME 'MD5Utils.getMD5(java.lang.String) return java.lang.String';
-- 包体实现
FUNCTION get_md5_hash(p_input_string IN VARCHAR2) RETURN VARCHAR2
IS
l_hash VARCHAR2(32);
BEGIN
l_hash := call_java_md5(p_input_string);
RETURN l_hash;
EXCEPTION
WHEN OTHERS THEN
-- 如果发生错误,返回 NULL 或一个特定的错误标识
RETURN 'ERROR: ' || SQLERRM;
END get_md5_hash;
PROCEDURE compute_md5_hash(
p_input_string IN VARCHAR2,
p_md5_hash OUT VARCHAR2,
p_error_msg OUT VARCHAR2
)
IS
BEGIN
p_md5_hash := call_java_md5(p_input_string);
p_error_msg := NULL;
EXCEPTION
WHEN OTHERS THEN
p_error_msg := 'Java Error: ' || SQLERRM;
p_md5_hash := NULL;
END compute_md5_hash;
END MD5_UTILS_PKG;
/
第 4 步:调用 Java 存储过程
一切准备就绪,我们可以调用它了!
调用带输出参数的存储过程
DECLARE
l_input VARCHAR2(100) := 'Hello, Oracle Java!';
l_hash VARCHAR2(32);
l_err VARCHAR2(4000);
BEGIN
MD5_UTILS_PKG.compute_md5_hash(
p_input_string => l_input,
p_md5_hash => l_hash,
p_error_msg => l_err
);
DBMS_OUTPUT.PUT_LINE('Input: ' || l_input);
DBMS_OUTPUT.PUT_LINE('MD5 Hash: ' || l_hash);
DBMS_OUTPUT.PUT_LINE('Error: ' || l_err);
-- 测试一个会触发异常的输入(如果 Java 代码有 bug)
-- MD5_UTILS_PKG.compute_md5_hash(p_input_string => NULL, p_md5_hash => l_hash, p_error_msg => l_err);
-- DBMS_OUTPUT.PUT_LINE('Error for NULL input: ' || l_err);
END;
/
预期输出:
Input: Hello, Oracle Java!
MD5 Hash: 6f1b2c3a4d5e6f7a8b9c0d1e2f3a4b5c
Error:
调用函数(更简洁)
SELECT
'Hello, Oracle Java!' AS input_string,
MD5_UTILS_PKG.get_md5_hash('Hello, Oracle Java!') AS md5_hash
FROM dual;
预期输出:
INPUT_STRING MD5_HASH
---------------------- ----------------------------------------
Hello, Oracle Java! 6f1b2c3a4d5e6f7a8b9c0d1e2f3a4b5c
(注意:上面的哈希值是示例,实际运行结果会不同)
重要注意事项和最佳实践
- 性能:首次调用 Java 方法时,Oracle 需要加载和编译 Java 类,可能会有轻微的性能开销,后续调用会快很多,对于高频调用的简单逻辑,纯 PL/SQL 可能更快。
- 调试:调试数据库中的 Java 代码比调试普通 Java 应用困难,可以使用
DBMS_JAVA.SET_OUTPUT(10000);将 Java 的System.out.println重定向到 SQL*Plus 的DBMS_OUTPUT,但这只适用于简单的调试,复杂的调试通常需要使用专门的工具。 - 安全性:将代码加载到数据库(
JAVA权限)和执行外部代码(JAVA权限)具有很高的权限,应谨慎授予这些权限,并确保你的 Java 代码是安全的,没有 SQL 注入或命令注入的风险。 - 依赖管理:如果你的 Java 代码需要外部的 JAR 文件(调用 Apache Commons),你需要使用
loadjava将 JAR 文件也加载到数据库中。loadjava -u scott/tiger@orcl -v -r my-library.jar
确保你的类路径在 Java 代码中正确设置。
- 数据类型映射:了解 Oracle PL/SQL 数据类型和 Java 数据类型之间的映射关系非常重要。
VARCHAR2/CHAR<->java.lang.StringNUMBER<->java.math.BigDecimalDATE/TIMESTAMP<->java.sql.Date/java.sql.TimestampBLOB/BFILE<->oracle.sql.BLOB/oracle.sql.BFILE
- 内存管理:在 Java 存储过程中创建大量对象时,要注意 Oracle 的内存管理,Java 堆和 PGA 内存是共享的,不当的内存使用可能导致数据库性能下降甚至崩溃,确保及时释放不再使用的对象。
