首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 端協調 Oracle 存儲過程串行執行:DBMS_LOCK 應用與 Wrapper 模式

     編輯:Java 相關知識

為什麼鎖必須在資料庫端

Java 端 synchronized 僅在單 JVM 內有效;叢集、重啟、容錯移轉時會失效。Oracle DBMS_LOCK 釋放機制綁定 Session/事務,應用掛掉 Session 斷開自動釋放,保證跨進程、跨節點串行化。

DBMS_LOCK 核心 API

API用途
ALLOCATE_UNIQUE(lockname, lockhandle, expiration_secs)邏輯名獲取唯一鎖句柄,多 Session 同名獲取同 handle
REQUEST(lockhandle, lockmode, timeout, release_on_commit)請求鎖,X_MODE 排他鎖,回傳 0 成功、1 逾時、2 死鎖、3 參數錯、4 已持有、5 非法 handle
RELEASE(lockhandle)顯式釋放鎖

方案一:PL/SQL Wrapper 封裝鎖邏輯(零侵入業務 SP)


CREATE OR REPLACE PACKAGE frm_test_task_pkg IS
  FUNCTION frm_test_task_func(i_lock_name VARCHAR2, i_procname VARCHAR2, ...) RETURN NUMBER;
END;

CREATE OR REPLACE PACKAGE BODY frm_test_task_pkg IS
  PROCEDURE app_task_wrapper_proc(i_procname VARCHAR2) AS
    cur INTEGER := DBMS_SQL.OPEN_CURSOR;
  BEGIN
    DBMS_SQL.PARSE(cur, 'BEGIN ' || i_procname || '; END;', DBMS_SQL.NATIVE);
    DBMS_SQL.EXECUTE(cur);
    DBMS_SQL.CLOSE_CURSOR(cur);
    COMMIT;
  END;

  FUNCTION frm_test_task_func(...) RETURN NUMBER IS
    v_lockhandle VARCHAR2(200); v_result NUMBER;
  BEGIN
    DBMS_LOCK.ALLOCATE_UNIQUE(i_lock_name, v_lockhandle, i_expiration_time);
    v_result := DBMS_LOCK.REQUEST(v_lockhandle, DBMS_LOCK.X_MODE, i_wait_time, TRUE);
    IF v_result = 0 THEN app_task_wrapper_proc(i_procname); END IF;
    RETURN v_result;
  END;
END;

Java 僅呼叫 frm_test_task_func('myLock', 'biz_proc'),動態 SQL 執行業務 SP,參數傳遞較繁瑣。

方案二:Java 端控制鎖生命週期(靈活、無侵入)


-- PL/SQL 僅提供鎖操作
CREATE OR REPLACE PACKAGE BODY frm_test_task_pkg IS
  PROCEDURE frm_test_lock_acquire(i_lock_name VARCHAR2, ..., o_result OUT NUMBER, o_lockhandle OUT VARCHAR2) IS
  BEGIN
    DBMS_LOCK.ALLOCATE_UNIQUE(i_lock_name, o_lockhandle, i_expiration_time);
    o_result := DBMS_LOCK.REQUEST(o_lockhandle, DBMS_LOCK.X_MODE, i_wait_time, FALSE);
  END;
  FUNCTION frm_test_lock_release(i_lockhandle VARCHAR2) RETURN NUMBER IS
  BEGIN RETURN DBMS_LOCK.RELEASE(i_lockhandle); END;
END;

// Java 模板模式
public abstract class LockedProcedureExecutor {
    private final String lockName;
    public LockedProcedureExecutor(String lockName) { this.lockName = lockName; }

    public final void execute(Connection conn) throws SQLException {
        String lockHandle = null;
        try {
            lockHandle = acquireLock(conn, lockName);
            doExecute(conn); // 子類實現具體 SP 呼叫
        } finally {
            if (lockHandle != null) releaseLock(conn, lockHandle);
        }
    }
    // acquireLock/releaseLock 呼叫 PL/SQL 函數
    // Connection 包裝防止子類誤關閉導致鎖未釋放
    protected abstract void doExecute(Connection conn) throws SQLException;
}

關鍵細節

  • release_on_commit=FALSE 由 Java 顯式釋放,避免事務提交前釋放導致併發衝突。
  • 連接池環境下,Connection.close() 僅歸還池,Session 不斷,鎖仍持有;需包裝 Connection 確保 close() 前釋放鎖,或使用模板方法統一控制。
  • 動態 SQL (DBMS_SQL) 雖通用但難調試、參數綁定繁瑣;建議業務 SP 參數固定化,Java 直接 CallableStatement 呼叫。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved