技術架構
Java Source (.java)
→ loadjava / SQL*Plus CREATE JAVA SOURCE
→ 數據庫內置JVM編譯為Class
→ CREATE FUNCTION/PROCEDURE ... LANGUAGE JAVA NAME '...' 發布為SQL可調用對象
完整實戰步驟
1. 編寫Java類 (Calc.java)
package oracle.java.demo;
public class Calc {
// 必須 public static,返回類型支援基本類型/String/BigDecimal/Date/oracle.sql.*
public static String square(int num) {
return num + "的平方為:" + (num * num);
}
// 多參數、異常拋出示例
public static double divide(double a, double b) throws ArithmeticException {
if (b == 0) throw new ArithmeticException("Divisor cannot be zero");
return a / b;
}
}
2. 加載到數據庫 (SQL*Plus)
-- 方法一:SQL*Plus直接加載源碼 (適合簡單類)
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "Calc" AS
package oracle.java.demo;
public class Calc {
public static String square(int num) {
return num + "的平方為:" + (num * num);
}
public static double divide(double a, double b) throws ArithmeticException {
if (b == 0) throw new ArithmeticException("Divisor cannot be zero");
return a / b;
}
}
/
-- 方法二:loadjava工具加載編譯後Class/JAR (適合複雜項目)
-- $ loadjava -user app_user/AppPass#123@ORCL -resolve -verbose Calc.class
-- 或打包JAR: $ loadjava -user ... -resolve app-utils.jar
3. 發布為SQL函數/過程
-- 發布 square 為函數
CREATE OR REPLACE FUNCTION java_square(p_num IN NUMBER) RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'oracle.java.demo.Calc.square(int) return java.lang.String';
/
-- 發布 divide 為函數
CREATE OR REPLACE FUNCTION java_divide(p_a IN NUMBER, p_b IN NUMBER) RETURN NUMBER
AS LANGUAGE JAVA
NAME 'oracle.java.demo.Calc.divide(double, double) return double';
/
-- 發布無返回過程示例
CREATE OR REPLACE PROCEDURE java_log(p_msg IN VARCHAR2)
AS LANGUAGE JAVA
NAME 'oracle.java.demo.Logger.log(java.lang.String)';
/
4. SQL調用驗證
SELECT java_square(10) FROM dual;
-- 10的平方為:100
SELECT java_divide(20, 4) FROM dual;
-- 5
BEGIN
java_log('Test from Java Stored Procedure');
END;
/
類型映射對照表 (Java ↔ Oracle SQL)
| Java類型 | Oracle SQL類型 | 備註 |
| int / Integer | NUMBER / INTEGER | 32位有符號 |
| long / Long | NUMBER | 64位 |
| float / Float | NUMBER / BINARY_FLOAT | |
| double / Double | NUMBER / BINARY_DOUBLE | |
| boolean / Boolean | NUMBER(1) / BOOLEAN(12c+) | 0/1映射 |
| String | VARCHAR2 / CLOB | 隱式轉換 |
| byte[] | RAW / BLOB | |
| java.sql.Date / java.time.LocalDate | DATE | |
| java.math.BigDecimal | NUMBER | 高精度計算首選 |
| oracle.sql.NUMBER / DATE / RAW | 對應原生類型 | 性能最佳、無轉換損耗 |
關鍵限制與最佳實踐
| 限制 | 規避方案 |
| 靜態方法只能訪問靜態字段 | 無狀態設計,依賴參數傳遞,避免實例字段 |
| 無法直接訪問數據庫會話狀態 | 通過JDBC默認連接new oracle.jdbc.defaultConnection()獲取會話上下文 |
| 異常只能拋出RuntimeException子類 | 業務異常包裝為SQLException或自定義RuntimeException |
| 性能:JVM啟動/上下文切換開銷 | 僅用於複雜計算/加密/外部API調用,簡單邏輯優先PL/SQL |
權限:需JAVAUSERPRIV、JAVASYSPRIV | 部署用戶授予GRANT JAVAUSERPRIV, JAVASYSPRIV TO app_user; |
架構決策:
• 「能用PL/SQL解決的,堅決不用Java」—— PL/SQL原生集成SQL、無上下文切換、性能優勢巨大。
• Java Stored Procedure適用場景:
1. 複雜數學/統計/加密算法(現成Java庫)
2. 調用外部REST/gRPC服務
3. 處理複雜文本/JSON/XML解析(Jackson/Gson)
4. 重用現有企業級Java組件庫
• 部署流程納入CI/CD:Maven編譯→loadjava→發布SQL腳本→自動化測試驗證。