Oracle Database 內建 **JVM (Aurora)**,支援在資料庫內直接運行 Java 程式碼,封裝為 **Java Stored Procedure (JSP)** 供 SQL/PL/SQL 呼叫。本文系統整理 **三種部署路徑**、**內部 JDBC 連線模式**、`loadjava` 工具鏈實戰與權限模型,助您打通 Java 生態與 Oracle 核心的最後一公里。
jdbc:default:connection:。
-- 1. 編譯並載入 Java Source
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "JavaDemo1" AS
import java.sql.*;
public class JavaDemo1 {
public static void main(String[] argv) {
System.out.println("Hello, Java Stored Procedure!");
}
}
/
-- 查詢編譯錯誤
SELECT * FROM USER_ERRORS WHERE TYPE = 'JAVA SOURCE' AND NAME = 'JavaDemo1';
-- 2. 建立 Call Spec (PL/SQL 包裝器)
CREATE OR REPLACE PROCEDURE javademo1 AS
LANGUAGE JAVA NAME 'JavaDemo1.main(java.lang.String[])';
/
-- 3. 執行
SET SERVEROUTPUT ON SIZE 5000
CALL DBMS_JAVA.SET_OUTPUT(5000); -- 重導向 System.out 到 SQL*Plus
CALL javademo1();
-- 輸出: Hello, Java Stored Procedure!
-- 1. 建立 Directory Object 指向 .class 檔所在目錄 (需 DBA 權限)
CREATE OR REPLACE DIRECTORY java_classes AS 'D:\oracle\java_classes';
GRANT READ ON DIRECTORY java_classes TO scott;
-- 2. 使用 BFILE 載入 Class
CREATE OR REPLACE JAVA CLASS USING BFILE (java_classes, 'OracleJavaProc.class');
/
-- 3. 建立 Call Spec
CREATE OR REPLACE PROCEDURE test_javaproc AS
LANGUAGE JAVA NAME 'OracleJavaProc.main(java.lang.String[])';
/
-- 4. 執行
SET SERVEROUTPUT ON SIZE 5000
CALL DBMS_JAVA.SET_OUTPUT(5000);
CALL test_javaproc();
# 1. 編寫完整 Java 類別 (含業務邏輯 + 內部 JDBC)
# OracleJavaProc.java
import java.sql.*;
import oracle.jdbc.*;
public class OracleJavaProc {
public static void addSalGrade(int grade, int losal, int hisal) throws SQLException {
System.out.println("Creating new salgrade for EMPLOYEE...");
try (Connection conn = DriverManager.getConnection("jdbc:default:connection:");
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO salgrade (GRADE, LOSAL, HISAL) VALUES (?, ?, ?)")) {
pstmt.setInt(1, grade);
pstmt.setInt(2, losal);
pstmt.setInt(3, hisal);
pstmt.executeUpdate();
} catch (SQLException e) {
System.err.println("ERROR! Adding Salgrade: " + e.getMessage());
throw e;
}
}
}
# 2. 編譯
javac OracleJavaProc.java
# 3. 遠程載入、編譯、解析 (一條龍)
loadjava -u scott/tiger@//host:1521/orclpdb -v -resolve OracleJavaProc.class
# -v: 詳細輸出; -resolve: 立即解析依賴; -genmissing: 自動生成缺失類別存根
# 4. 驗證載入狀態
SELECT object_name, object_type, status
FROM user_objects
WHERE object_type LIKE 'JAVA%';
-- 輸出: JAVA SOURCE VALID, JAVA CLASS VALID
# 5. 建立 Call Spec
CREATE OR REPLACE PROCEDURE add_salgrade(
p_grade NUMBER, p_losal NUMBER, p_hisal NUMBER
) AS LANGUAGE JAVA NAME 'OracleJavaProc.addSalGrade(int, int, int)';
/
# 6. 執行驗證
SET SERVEROUTPUT ON SIZE 2000
CALL DBMS_JAVA.SET_OUTPUT(2000);
EXEC add_salgrade(6, 10000, 15000);
SELECT * FROM salgrade WHERE grade = 6;
-- 輸出: GRADE=6, LOSAL=10000, HISAL=15000
jdbc:default:connection: (固定,不可變)
-- 授予檔案系統讀寫權限
EXEC DBMS_JAVA.GRANT_PERMISSION('SCOTT', 'SYS:java.io.FilePermission', '/tmp/*', 'read,write');
-- 授予網路連線權限 (連外部 API)
EXEC DBMS_JAVA.GRANT_PERMISSION('SCOTT', 'SYS:java.net.SocketPermission', 'api.example.com:443', 'connect,resolve');
-- 查詢權限
SELECT * FROM USER_JAVA_POLICY WHERE GRANTEE = 'SCOTT';
原則:最小權限、審計追蹤、生產環境嚴格管控 `SYS:java.security.AllPermission`。
| Java Type | SQL/PLSQL Type | 備註 |
|---|---|---|
| int / Integer | NUMBER / PLS_INTEGER | |
| long / Long | NUMBER | |
| double / Double | NUMBER / BINARY_DOUBLE | |
| String | VARCHAR2 / CLOB | 長字串用 CLOB |
| byte[] | RAW / BLOB | |
| java.sql.Date / Timestamp | DATE / TIMESTAMP | |
| ResultSet | REF CURSOR (OUT) | 僅支援 OUT 參數 |
| String[] / Array | VARRAY / NESTED TABLE | 需定義 SQL Type |
| 場景 | 建議 |
|---|---|
| 大量資料處理 | 優先用純 PL/SQL (FORALL, BULK COLLECT) 或 SQL;Java 僅用於複雜邏輯、外部 API 呼叫、加密演算等 PL/SQL 弱項 |
| Session 狀態洩漏 | 靜態變數會跨呼叫殘留 (JVM 進程級);**嚴禁**在靜態欄位保存連線、Statement、業務狀態 |
| 編譯錯誤難除錯 | 善用 SELECT * FROM USER_ERRORS WHERE TYPE='JAVA SOURCE'...;`loadjava -v` 觀察詳細解析日誌 |
| 版本升級相容性 | Oracle 升級 (如 11g→19c) 可能導致 Java 類別無效;升級後執行 UTL_RECOMP.RECOMP_SERIAL() 重編譯 |
| 除錯困難 | 使用 `DBMS_JAVA.SET_OUTPUT` 導向 `DBMS_OUTPUT`;或遠程除錯 (JPDA) 需開放端口、配置 `java_debug` 參數 |
掌握 `loadjava` 部署流程、內部 JDBC 模式與權限模型,讓 Java 成為 Oracle 資料庫的**強力擴充引擎**,而非單純的外部應用語言。