首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Oracle Web 應用開發:PL/SQL + HTP/HTF 套件構建動態網頁實戰

     編輯:Oracle 資料庫相關知識

前言:資料庫內建 Web 開發框架的鼻祖

Oracle 8i/9i 時代,Oracle Web Server (OWS) + PL/SQL Cartridge (mod_plsql) 讓資料庫直接輸出 HTML,無需中間層 Java/.NET。核心在於 HTP (Hypertext Procedures)、HTF (Hypertext Functions) 套件,以及 OWA_UTIL、OWA_COOKIE、OWA_SEC 等內建工具包。本文以「學生成績查詢」為例,完整演示從資料庫表到動態網頁的端到端開發流程。

架構原理

Browser (HTTP Request)
    ↓
Oracle HTTP Server (Apache + mod_plsql)
    ↓ 認證/會話管理/連接池
Oracle Database (PL/SQL Procedure)
    ↓ HTP/HTF 輸出 HTML
Browser (HTTP Response: HTML)

URL 映射規則:https://host:port/pls/DAD/PROCEDURE_NAME?PARAM=VALUE

  • DAD (Database Access Descriptor):連接池、認證、Session 管理配置單元。
  • PROCEDURE_NAME:PL/SQL 存儲過程/函數入口點。
  • PARAM:自動綁定為過程參數 (名稱大小寫不敏感)。

核心套件:HTP (程序式) vs HTF (函數式)

套件風格用法適用場景
HTP程序 (Procedure)HTP.PRINT('<h1>Title</h1>'); 直接寫入 HTTP 回應緩衝區大段 HTML 輸出、邏輯控制流中穿插輸出
HTF函數 (Function)v_html := HTF.ANCHOR('url','Link Text'); 回傳 HTML 字串組裝 HTML 片段、函數式風格、賦值給變量

兩者功能對等,HTP.xxx 對應 HTF.xxx,常用:HTMLOPEN/HTMLCLOSE, HEADOPEN/HEADCLOSE, TITLE, BODYOPEN/BODYCLOSE, TABLEOPEN/TABLECLOSE, TABLEROWOPEN/TABLEROWCLOSE, TABLEHEADER, TABLEDATA, FORMOPEN/FORMCLOSE, FORMTEXT, FORMSUBMIT, ANCHOR, IMG, PRINT/PRN (PRN 不換行)。

實戰案例:動態成績查詢網頁

1. 資料庫表結構

CREATE TABLE student_scores (
    student_id   NUMBER PRIMARY KEY,
    student_name VARCHAR2(50),
    subject      VARCHAR2(30),
    score        NUMBER(5,2),
    exam_date    DATE
);

-- 測試資料
INSERT INTO student_scores VALUES (1001, '張三', '數學', 92.5, DATE '2024-01-15');
INSERT INTO student_scores VALUES (1001, '張三', '英文', 88.0, DATE '2024-01-15');
INSERT INTO student_scores VALUES (1002, '李四', '數學', 76.0, DATE '2024-01-15');
-- ...

2. 入口過程:輸入學號查詢表單

CREATE OR REPLACE PROCEDURE score_query_form IS
BEGIN
    HTP.HTMLOPEN;
    HTP.HEADOPEN;
    HTP.TITLE('學生成績查詢系統');
    HTP.HEADCLOSE;
    HTP.BODYOPEN(cattributes => 'bgcolor="#F0F8FF"');
    HTP.CENTEROPEN;
    HTP.HEADER(1, '學生成績查詢');
    HTP.CENTERCLOSE;
    HTP.FORMOPEN(url => 'score_query_result', method => 'POST');
    HTP.TABLEOPEN(cattributes => 'border="1" align="center" cellpadding="5"');
    HTP.TABLEROWOPEN;
    HTP.TABLEHEADER('輸入學號:');
    HTP.TABLEDATA(HTF.FORMTEXT(cname => 'p_student_id', csize => 10, cmaxlength => 10));
    HTP.TABLEROWCLOSE;
    HTP.TABLEROWOPEN;
    HTP.TABLEDATA(HTF.FORMSUBMIT(cvalue => '查詢', cattributes => 'style="font-weight:bold"'), cattributes => 'colspan="2" align="center"');
    HTP.TABLEROWCLOSE;
    HTP.TABLECLOSE;
    HTP.FORMCLOSE;
    HTP.BODYCLOSE;
    HTP.HTMLCLOSE;
END score_query_form;
/

3. 結果過程:接收參數、查詢資料、輸出動態表格

CREATE OR REPLACE PROCEDURE score_query_result (p_student_id IN NUMBER) IS
    CURSOR c_scores IS
        SELECT subject, score, exam_date
        FROM student_scores
        WHERE student_id = p_student_id
        ORDER BY exam_date DESC, subject;
    v_student_name VARCHAR2(50);
    v_row_count    NUMBER := 0;
BEGIN
    -- 獲取學生姓名 (單獨查詢或 JOIN)
    SELECT student_name INTO v_student_name
    FROM student_scores
    WHERE student_id = p_student_id
    AND ROWNUM = 1;
    
    HTP.HTMLOPEN;
    HTP.HEADOPEN;
    HTP.TITLE(v_student_name || ' 的成績單');
    HTP.HEADCLOSE;
    HTP.BODYOPEN(cattributes => 'bgcolor="#FFFFFF"');
    HTP.CENTEROPEN;
    HTP.HEADER(2, v_student_name || ' (學號: ' || p_student_id || ') 的成績查詢結果');
    HTP.CENTERCLOSE;
    
    HTP.TABLEOPEN(cattributes => 'border="1" align="center" cellpadding="5" cellspacing="0" width="60%"');
    HTP.TABLEROWOPEN;
    HTP.TABLEHEADER('科目', cattributes => 'bgcolor="#4472C4" style="color:white"');
    HTP.TABLEHEADER('分數', cattributes => 'bgcolor="#4472C4" style="color:white"');
    HTP.TABLEHEADER('考試日期', cattributes => 'bgcolor="#4472C4" style="color:white"');
    HTP.TABLEROWCLOSE;
    
    FOR r IN c_scores LOOP
        v_row_count := v_row_count + 1;
        HTP.TABLEROWOPEN(cattributes => CASE MOD(v_row_count,2) WHEN 0 THEN 'bgcolor="#E2EFDA"' ELSE 'bgcolor="#FFFFFF"' END);
        HTP.TABLEDATA(r.subject, cattributes => 'align="center"');
        HTP.TABLEDATA(r.score, cattributes => 'align="center"');
        HTP.TABLEDATA(TO_CHAR(r.exam_date, 'YYYY-MM-DD'), cattributes => 'align="center"');
        HTP.TABLEROWCLOSE;
    END LOOP;
    
    IF v_row_count = 0 THEN
        HTP.TABLEROWOPEN;
        HTP.TABLEDATA('查無資料', cattributes => 'colspan="3" align="center" style="color:red"');
        HTP.TABLEROWCLOSE;
    END IF;
    
    HTP.TABLECLOSE;
    HTP.PRINT('
' || HTF.ANCHOR('score_query_form', '返回重新查詢') || '
'); HTP.BODYCLOSE; HTP.HTMLCLOSE; EXCEPTION WHEN NO_DATA_FOUND THEN HTP.PRINT('

學號 ' || p_student_id || ' 不存在

'); HTP.PRINT('
' || HTF.ANCHOR('score_query_form', '返回重新查詢') || '
'); WHEN OTHERS THEN HTP.PRINT('

系統錯誤: ' || SQLERRM || '

'); -- 記錄錯誤日誌 INSERT INTO error_log (proc_name, error_msg, log_time) VALUES ('SCORE_QUERY_RESULT', SQLERRM, SYSDATE); END score_query_result; /

4. 部署與驗證

  1. 編譯過程:SQL> @score_procs.sql (無錯誤)。
  2. 配置 DAD (Enterprise Manager 或 $ORACLE_HOME/Apache/modplsql/cfg/dads.conf):

    SetHandler pls_handler
    Order deny,allow
    Allow from all
    PlsqlDatabaseUsername scott
    PlsqlDatabasePassword tiger
    PlsqlDatabaseConnectString orcl:1521:ORCL
    PlsqlAuthenticationMode Basic
    PlsqlDefaultPage score_query_form
    PlsqlDocumentTablename scott.wwv_flow_documents
    PlsqlDocumentPath docs
    PlsqlDocumentProcedure wwv_flow_doc_manager.process_download

  1. 重啟 OHS:opmnctl restartproc type=ohs。
  2. 瀏覽器訪問:https://host:port/pls/score_dad/score_query_form。

進階主題:狀態管理、安全、效能

主題解決方案關鍵套件/技術
Session 狀態OWA_UTIL.GET_CGI_ENV('HTTP_COOKIE') 解析、OWA_COOKIE.SEND/GET、PL/SQL Package 變量 (Session 級)OWA_COOKIE, OWA_UTIL, Package State
認證授權DAD 配置 PlsqlAuthenticationMode Basic/Custom/Owa、OWA_CUSTOM.AUTHORIZE 函數mod_plsql DAD, OWA_CUSTOM
防 XSS/注入HTP/HTF 自動 HTML 轉義 (PRINT/PRN)、綁定變量參數化、OWA_UTIL.ESCAPE_URLHTP/HTF, Bind Variables
效能優化mod_plsql 連接池 (PlsqlMaxRequestsPerSession, PlsqlIdleSessionCleanupInterval)、緩存輸出 (OWA_CACHE)、靜態資源剝離至 Web Servermod_plsql config, OWA_CACHE
檔案上傳/下載OWA_UTIL.GET_OWA_SERVICE_PATH、WP_G_DOCLOAD.DOWNLOAD_FILE、Document TableWP_G_DOCLOAD, Document Table

現代視角:為何已被淘汰?

  • 關注點未分離:業務邏輯、資料存取、展示邏輯混雜在 PL/SQL 中,難以單元測試、版本控制、前端框架整合。
  • 前端能力受限:無法輕鬆支援 AJAX、SPA (React/Vue/Angular)、響應式設計、Web Components。
  • 擴展性瓶頸:資料庫連接池承載 Web 請求,高併發下搶佔業務連接,影響核心交易效能。
  • 人才斷層:PL/SQL Web 開發技能稀缺,現代全棧工程師以 Java/Node.js/Python/Go 為主。

遷移路徑:PL/SQL 過程 → REST API (ORDS / APEX REST / 自建 Spring Boot/Node.js) → 前端獨立部署。ORDS (Oracle REST Data Services) 可自動將 PL/SQL 過程發布為 REST 端點,平滑遷移。

總結

HTP/HTF + mod_plsql 是 Oracle 將資料庫變身為應用伺服器的輝嘗試,曾支撐無數 9i/10g 時代的內部系統。理解其「URL→DAD→PL/SQL→HTP→HTML」流程、掌握 HTP.TABLE* 動態表格生成、OWA_* 狀態管理、mod_plsql 連接池調優,對維護存量系統、規劃遷移路徑仍具高價值。新架構請擁抱 資料庫專注資料、中間層專注業務、前端專注體驗 的關注點分離原則。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved