核心概念
Oracle 数据库允许你将 Java 类编译后加载到数据库中,然后在存储过程、函数或 SQL 语句中直接调用这些 Java 方法,这个功能被称为 Java in Database 或 JVM (Java Virtual Machine) in Oracle。

其工作流程如下:
- 编写 Java 代码:创建一个 Java 类,并定义需要被调用的方法(通常是
public static方法)。 - 编译 Java 代码:使用
javac将.java文件编译成.class字节码文件。 - 加载到数据库:使用 Oracle 的
loadjava命令行工具或 SQL 语句,将.class文件加载到 Oracle 数据库中,形成一个 Java 源 对象。 - 创建 SQL 代理:使用
CREATE JAVA和CREATE OR REPLACE AND COMPILE JAVA SOURCE WITH ...语句,或者直接使用CREATE OR REPLACE AND RESOLVE JAVA SOURCE来加载代码。CREATE OR REPLACE AND RESOLVE会在加载时自动解析依赖。 - 创建 PL/SQL 封装器:创建一个 PL/SQL 存储过程或函数,作为外部世界(PL/SQL 或 SQL)调用 Java 代码的桥梁,这个封装器使用
CALL语句来执行 Java 方法。 - 调用存储过程:像调用普通的 PL/SQL 存储过程一样,调用你刚刚创建的封装器。
详细步骤与示例
假设我们的需求是:在 Oracle 数据库中调用一个 Java 方法,该方法接收一个字符串,然后返回该字符串的 MD5 哈希值。
第 1 步:编写 Java 代码
创建一个名为 MD5Utils.java 的文件,注意,方法必须是 public static 的。
// MD5Utils.java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Utils {
/**
* 计算字符串的 MD5 哈希值
* @param input 输入字符串
* @return MD5 哈希字符串 (十六进制)
*/
public static String getMD5(String input) {
try {
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();
} catch (NoSuchAlgorithmException e) {
// 在数据库环境中,抛出 RuntimeException 会被 PL/SQL 捕获
throw new RuntimeException("MD5 algorithm not found.", e);
}
}
}
第 2 步:编译 Java 代码
打开命令行,使用 JDK 的 javac 编译。

# 确保你的 JAVA_HOME 和 PATH 环境变量已正确设置 javac MD5Utils.java
执行成功后,会生成 MD5Utils.class 文件。
第 3 步:将 Java 类加载到 Oracle 数据库
这里提供两种常用方法:
使用 loadjava 命令行工具(推荐)
这是最简单直接的方法,你需要先配置好 ORACLE_HOME 和 PATH 环境变量。
# 连接到数据库并执行加载 # 格式: loadjava -user 用户名/密码 -resolve MD5Utils.class # 示例 (以 sys 用户连接,并授予必要的权限) loadjava -user sys/oracle@localhost:1521/ORCLCDB as sysdba -resolve MD5Utils.class
-user: 指定数据库用户和密码。-resolve: 非常重要!这个选项会在加载 Java 类的同时,解析其所有依赖关系(如java.security.MessageDigest),并检查语法错误,如果缺少依赖或代码有误,加载会失败。as sysdba: 如果需要高权限(如安装到SYS模式下),需要使用此选项。
在 SQL*Plus 或 SQL Developer 中使用 CREATE JAVA 语句
如果你无法使用命令行,可以直接在 SQL 客户端中操作。

- 读取 .class 文件内容:
你需要使用一个工具(如 UTL_RAW.CAST_TO_VARCHAR2 配合 DBMS_LOB)来读取二进制
.class文件并将其转换为 Base64 或 Hex 字符串,然后插入到JAVA$CLASS$MD5UTILS表中(这个表是loadjava自动创建的),这个过程非常繁琐,强烈推荐使用loadjava。
第 4 步:验证 Java 类是否加载成功
查询 USER_OBJECTS 视图来检查 Java 源对象是否存在。
SELECT object_name, object_type, status FROM user_objects WHERE object_name = 'MD5UTILS';
你应该能看到类似以下的输出,STATUS 应为 VALID。
| OBJECT_NAME | OBJECT_TYPE | STATUS |
|---|---|---|
| MD5UTILS | JAVA CLASS | VALID |
第 5 步:创建 PL/SQL 封装器(存储过程)
这是连接 PL/SQL 和 Java 的关键一步,我们将创建一个名为 PKG_UTIL 的包,其中包含一个 GET_MD5_HASH 过程。
CREATE OR REPLACE PACKAGE PKG_UTIL AS -- 声明一个公共过程,用于调用 Java 方法 PROCEDURE GET_MD5_HASH(p_input_string IN VARCHAR2, p_hash_value OUT VARCHAR2); END PKG_UTIL; /
创建包体,实现这个过程:
CREATE OR REPLACE PACKAGE BODY PKG_UTIL AS
PROCEDURE GET_MD5_HASH(p_input_string IN VARCHAR2, p_hash_value OUT VARCHAR2) AS
-- 声明一个 Java 对象,指向我们加载的类
LANGUAGE JAVA NAME 'MD5Utils.getMD5(java.lang.String) return java.lang.String';
BEGIN
-- 调用 Java 方法,并将结果赋给 OUT 参数
-- 注意:这里的语法是 CALL ... INTO ...
-- Java 方法有返回值,使用 INTO 子句
-- Java 方法是 void,则不需要 INTO 子句
CALL MD5Utils.getMD5(p_input_string) INTO p_hash_value;
END GET_MD5_HASH;
END PKG_UTIL;
/
代码解释:
LANGUAGE JAVA NAME '...': 这是 PL/SQL 和 Java 之间的“胶水”,它告诉 Oracle 如何映射 PL/SQL 调用到底层的 Java 方法。MD5Utils: Java 的类名。.getMD5: Java 的方法名。(java.lang.String): Java 方法的参数类型,即使 Java 的基本类型String,在 PL/SQL 中也必须使用其全限定名java.lang.String。return java.lang.String: Java 方法的返回值类型,同样,必须使用全限定名。
CALL ... INTO ...: 这是执行 Java 方式的标准 PL/SQL 语法,Java 方法有返回值,必须使用INTO子句来接收它。
第 6 步:调用 PL/SQL 存储过程
你可以像调用任何其他 PL/SQL 过程一样来调用它了。
SET SERVEROUTPUT ON;
DECLARE
v_input VARCHAR2(100) := 'Hello, Oracle!';
v_md5_hash VARCHAR2(32);
BEGIN
-- 调用我们创建的包过程
PKG_UTIL.GET_MD5_HASH(p_input_string => v_input, p_hash_value => v_md5_hash);
DBMS_OUTPUT.PUT_LINE('原始字符串: ' || v_input);
DBMS_OUTPUT.PUT_LINE('MD5 哈希值: ' || v_md5_hash);
END;
/
预期输出:
原始字符串: Hello, Oracle!
MD5 哈希值: 6f5a0d9f0b4e3a6b5c7d8e9f0a1b2c3d
(注意:实际哈希值可能因实现细节略有不同,但格式应为32位十六进制字符串)
关键点与最佳实践
-
权限管理:
- 执行 Java 代码的用户需要
CREATE PROCEDURE权限(用于创建 PL/SQL 封装器)。 - 为了加载 Java 代码,用户通常需要
JAVAUSERPRIV权限,这个权限允许用户创建、替换、删除和执行自己的 Java 类。 GRANT JAVAUSERPRIV TO your_user;
- 执行 Java 代码的用户需要
-
错误处理:
- Java 代码抛出
RuntimeException,PL/SQL 会将其包装成一个ORA-29532: Java call terminated by uncaught Java exception错误。 - 你可以在 PL/SQL 中使用
EXCEPTION块来捕获这个错误,并使用SQLERRM获取原始的 Java 异常信息。 - 最佳实践:在 Java 代码中做好异常处理,避免将底层的技术细节(如
NoSuchAlgorithmException)直接抛出给 PL/SQL 层,可以将其封装为一个业务相关的异常或返回一个错误代码。
- Java 代码抛出
-
性能考量:
- 首次调用有开销:当 Java 类第一次被调用时,Oracle JVM 会进行类加载和验证,这比后续调用要慢。
- 避免频繁调用:对于简单的、性能要求高的操作(如字符串拼接、数值计算),直接使用 PL/SQL 内置函数比调用 Java 要快得多,Java 调用更适合复杂的算法、外部系统集成或 PL/SQL 难以实现的功能。
- SQL 中的直接调用:如果你的 Java 方法是纯计算且没有副作用,你可以直接在 SQL 语句中调用它,而无需 PL/SQL 封装器,前提是你需要授予该用户
EXECUTE权限在你的 Java 类上。
-- 授予执行权限 GRANT EXECUTE ON MD5UTILS TO your_user; -- 直接在 SQL 中调用 SELECT 'Hello, Oracle!' AS original_text, MD5Utils.getMD5('Hello, Oracle!') AS md5_hash FROM dual; -
依赖管理:
- 始终使用
loadjava -resolve或CREATE OR REPLACE AND RESOLVE JAVA SOURCE,这样可以确保所有依赖的 Java 类库都已正确加载并可用,避免运行时NoClassDefFoundError。
- 始终使用
-
安全性:
- 运行在数据库中的 Java 代码拥有与数据库用户相同的权限,如果以高权限用户(如
SYS)加载了有安全漏洞的 Java 代码,可能会对整个数据库构成威胁,只加载可信的、经过充分测试的代码。
- 运行在数据库中的 Java 代码拥有与数据库用户相同的权限,如果以高权限用户(如
