核心概念:Oracle JVM
Oracle 数据库自带了一个完整的、与数据库集成的 JVM,这意味着你可以在数据库中:

- 编译 Java 源代码:
CREATE AND REPLACE JAVA ... SOURCE ... - 加载 Java 类文件:
CREATE AND REPLACE JAVA ... RESOLVER ... - 在 SQL 或 PL/SQL 中调用 Java 方法:通过
CALL语句或CREATE OR REPLACE AND COMPILE JAVA SOURCE ... AS ...后,定义一个 PL/SQL 外部过程来调用。
调用 Java 的两种主要方式
通常有两种主流方式来调用 Java:
- 通过 SQL 的
CALL语句:这种方式最直接,适合简单的、无状态的 Java 方法调用,它不依赖于 PL/SQL 环境。 - 通过 PL/SQL 过程/函数:这是最常用、最灵活的方式,它允许你将 Java 方法封装成一个 PL/SQL 的接口,这样 PL/SQL 代码块、其他存储过程、触发器等都可以像调用普通 PL/SQL 过程一样调用它,这种方式也便于管理权限和错误处理。
我们将重点讲解第二种方式,因为它在实际应用中更为普遍。
详细步骤:通过 PL/SQL 调用 Java
整个过程可以分为以下几个步骤:
步骤 1:编写 Java 代码
你编写标准的 Java 代码,为了能被 PL/SQL 调用,Java 方法必须是 public static 的。

示例:HelloWorld.java
// 这个类将定义一个可以被 PL/SQL 调用的方法
public class HelloWorld {
// PL/SQL 只能调用 public static 方法
// 方法参数和返回值类型必须是 Java 基本类型或 Oracle 可以自动映射的类型
public static String sayHello(String name) {
return "Hello from Java, " + name + "!";
}
// 一个返回数字的示例
public static int addNumbers(int a, int b) {
return a + b;
}
}
步骤 2:在数据库中创建 Java 类源代码
使用 CREATE AND JAVA SOURCE 语句将 Java 源代码加载到数据库中。
CREATE AND REPLACE JAVA SOURCE named "HelloWorld" AS
-- 将上面 Java 代码的完整内容粘贴到这里
-- 注意:不要包含 'public class HelloWorld' 这一行,因为 NAME 子句已经指定了类名
-- 但实际上,包含进去通常也没问题,数据库会处理。
-- 最佳实践是粘贴完整的源代码。
public class HelloWorld {
public static String sayHello(String name) {
return "Hello from Java, " + name + "!";
}
public static int addNumbers(int a, int b) {
return a + b;
}
};
/
CREATE AND REPLACE:如果类已存在,则替换它。JAVA SOURCE named "HelloWorld":指定 Java 类的完全限定名(包名+类名),这里我们用了简单的类名。AS ... ;:Java 源代码的开始和结束。- 在 SQL*Plus 或类似工具中,执行该语句。
步骤 3:编译 Java 类
使用 ALTER JAVA 语句来编译加载的 Java 源代码。
ALTER JAVA SOURCE "HelloWorld" COMPILE;
- 如果编译成功,不会有任何输出。
- 如果有编译错误,Oracle 会返回详细的错误信息,包括行号和错误原因。
步骤 4:创建 PL/SQL 外部过程(调用器)
这是连接 PL/SQL 和 Java 的关键桥梁,我们需要创建一个 PL/SQL 函数或过程,它内部会调用 LOADJAVA 工具生成的调用接口,或者更直接地,使用 CALL 语句。

最简单的方式是使用 JAVA 关键字和 CALL 语句。
示例:创建一个 PL/SQL 函数 call_java_say_hello
CREATE OR REPLACE FUNCTION call_java_say_hello(p_name IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE JAVA NAME 'HelloWorld.sayHello(java.lang.String) return java.lang.String'; /
代码解析:
CREATE OR REPLACE FUNCTION ...:定义一个 PL/SQL 函数。AS LANGUAGE JAVA:指定这是一个 Java 外部函数。NAME '...':这是最核心的部分,它定义了 PL/SQL 和 Java 方法之间的映射关系。HelloWorld.sayHello:要调用的 Java 类名和方法名。(java.lang.String):Java 方法的参数类型,即使String是核心类型,也最好使用完全限定名java.lang.String。return java.lang.String:Java 方法的返回值类型。
示例:创建一个 PL/SQL 函数 call_java_add_numbers
CREATE OR REPLACE FUNCTION call_java_add_numbers(p_a IN NUMBER, p_b IN NUMBER) RETURN NUMBER AS LANGUAGE JAVA NAME 'HelloWorld.addNumbers(int, int) return int'; /
- 注意类型映射:
VARCHAR2(PL/SQL) <->java.lang.String(Java)NUMBER(PL/SQL) <->java.lang.Number(Java) 或int,double等,对于整数,NUMBER会自动映射到int或java.lang.Integer。DATE(PL/SQL) <->java.sql.Date(Java)BLOB(PL/SQL) <->java.sql.Blob(Java)
步骤 5:调用和测试
你可以像调用任何普通的 PL/SQL 函数一样调用它。
-- 测试 sayHello 函数
SELECT call_java_say_hello('PL/SQL Developer') AS greeting FROM DUAL;
-- 输出:
-- GREETING
-- ----------------------------------------
-- Hello from Java, PL/SQL Developer!
-- 测试 addNumbers 函数
SELECT call_java_add_numbers(10, 25) AS sum_result FROM DUAL;
-- 输出:
-- SUM_RESULT
-- ----------------
-- 35
高级示例:在 Java 中访问数据库
Java 代码最强大的地方在于它可以访问数据库,这需要使用 JDBC,在 Oracle JVM 中,有一个特殊的驱动类 oracle.jdbc.driver.OracleDriver,它允许 Java 代码直接连接到运行它的同一个数据库。
示例:DatabaseUtils.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DatabaseUtils {
// 这个方法会查询一个表并返回结果
public static String getEmpNameFromId(int empId) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
String empName = "Not Found";
try {
// 1. 加载 JDBC 驱动
// 注意:这是 Oracle JVM 内置的驱动,不需要额外的 jar 包
Class.forName("oracle.jdbc.driver.OracleDriver");
// 2. 获取数据库连接
// "jdbc:default:connection" 是一个特殊的 URL,表示连接到当前数据库会话
conn = DriverManager.getConnection("jdbc:default:connection", "", "");
// 3. 创建语句
stmt = conn.createStatement();
String sql = "SELECT ename FROM scott.emp WHERE empno = " + empId;
// 4. 执行查询
rs = stmt.executeQuery(sql);
// 5. 处理结果
if (rs.next()) {
empName = rs.getString("ename");
}
} catch (ClassNotFoundException | SQLException e) {
// 在实际应用中,应该记录更详细的错误信息
// 这里简单返回错误信息
empName = "Error: " + e.getMessage();
} finally {
// 6. 关闭资源
try { if (rs != null) rs.close(); } catch (SQLException e) {}
try { if (stmt != null) stmt.close(); } catch (SQLException e) {}
try { if (conn != null) conn.close(); } catch (SQLException e) {}
}
return empName;
}
}
操作步骤:
-
创建 Java 源代码
CREATE AND REPLACE JAVA SOURCE named "DatabaseUtils" AS -- 将上面的 Java 代码完整粘贴到这里 ... (完整的 DatabaseUtils.java 代码) ... ; /
-
编译 Java 源代码
ALTER JAVA SOURCE "DatabaseUtils" COMPILE;
-
创建 PL/SQL 调用器函数
CREATE OR REPLACE FUNCTION get_employee_name(p_emp_id IN NUMBER) RETURN VARCHAR2 AS LANGUAGE JAVA NAME 'DatabaseUtils.getEmpNameFromId(int) return java.lang.String'; /
-
测试(确保你有
scott模式,或者修改表名和模式)-- 假设 scott.emp 表存在 SELECT get_employee_name(7369) AS employee_name FROM DUAL; -- 输出: -- EMPLOYEE_NAME -- ---------------- -- SMITH
权限管理
执行这些操作需要特定的权限。
-
创建 Java 类:用户需要
CREATE JAVA权限。GRANT CREATE JAVA TO your_user;
-
外部网络访问:如果你的 Java 代码需要访问数据库以外的网络资源(如调用 Web 服务),你需要:
- 数据库级别:以
DBA身份执行dbms_java.grant_permission('YOUR_USER', 'java.net.SocketPermission', '<<ALL HOSTS>>', 'connect,resolve'); - JVM 级别:需要设置
JServer的oracle.network.ajp.AJPAdapter的ListenAddress和ListenPort,这通常由 DBA 完成。
- 数据库级别:以
工具和简化操作
手动执行 CREATE JAVA SOURCE 和 ALTER JAVA ... COMPILE 比较繁琐,你可以使用更高效的方法:
-
使用
loadjava命令行工具: 这是一个命令行工具,可以直接将.java、.class或.jar文件加载到数据库中并自动编译。# 将 HelloWorld.class 加载到数据库中 loadjava -user your_user/your_password@your_db HelloWorld.class
-
使用 SQL Developer: 在 SQL Developer 中,右键点击 "Java" 文件夹 -> "Create Java" -> "Class",可以直接在图形界面中编写和编译 Java 代码,非常方便。
| 步骤 | 操作 | 命令/语句 | 描述 |
|---|---|---|---|
| 1 | 编写 Java | IDE (如 IntelliJ, Eclipse) | 编写 public static 方法。 |
| 2 | 加载源码 | CREATE AND REPLACE JAVA SOURCE ... AS ... |
将 Java 代码存入数据库。 |
| 3 | 编译 | ALTER JAVA SOURCE "ClassName" COMPILE; |
编译 Java 源码为字节码。 |
| 4 | 创建调用器 | CREATE OR REPLACE FUNCTION ... AS LANGUAGE JAVA NAME '...'; |
创建 PL/SQL 接口,映射 Java 方法。 |
| 5 | 调用 | SELECT your_function(...) FROM DUAL; |
像调用普通 PL/SQL 函数一样使用。 |
通过以上步骤,你就可以在 Oracle 存储过程中灵活地调用 Java 代码,极大地扩展了数据库应用的功能边界。
