Oracle DBMS_SESSION SET_CONTEXT 不存储值

标签 oracle session global-variables

我正在尝试在 Oracle 中创建一个系统,其中上下文存储值;如果在任何 session 中更新(更新/插入/删除)表,则应增加该值。我遇到的问题是,即使我确信我已经正确设置了它,但它似乎不起作用 - 上下文似乎没有实际存储该值。我使用的是 Oracle 11.2.0.1.0。

对于最小可能的示例:

我有一个上下文(ACCESSED GLOBALY 子句应该使它能够在所有 Oracle session 中访问这些值,这正是我想要的):

CREATE OR REPLACE CONTEXT MM_CONTEXT USING PCKG_TESTGLOBALS ACCESSED GLOBALLY;

我有一个调试表:

CREATE TABLE DATALOG (
  DATALOG_SEQ NUMBER,
  AT_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  MESSAGE VARCHAR2(4000)
);

我有一个序列来支持 DATALOG 表:

CREATE SEQUENCE SQ_DATALOG;

现在需要 PCKG_TESTGLOBALS 包,它可以访问 MM_CONTEXT 上下文并可以更新其中的元素:

CREATE OR REPLACE PACKAGE PCKG_TESTGLOBALS IS

  PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2);
  PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2);

  FUNCTION GetTABLEID RETURN NUMBER;

END PCKG_TESTGLOBALS;
/

CREATE OR REPLACE PACKAGE BODY PCKG_TESTGLOBALS IS

  CONTEXT_NAME CONSTANT VARCHAR2(100) := 'MM_CONTEXT';

  PROCEDURE Log(FunctionName IN VARCHAR2, Msg IN VARCHAR2) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
  BEGIN
    INSERT INTO DATALOG(DATALOG_SEQ, MESSAGE) VALUES (SQ_DATALOG.NEXTVAL, FunctionName || ':' || Msg);
    COMMIT;
  END;

  PROCEDURE SetParameter(p_name IN VARCHAR2, p_value IN VARCHAR2) IS
    ActualValue VARCHAR2(10000);
  BEGIN
    Log('SetParameter', 'ENTERED');
    Log('SetParameter', 'SETTING "' || p_name || '" TO "' || p_value || '"');
    DBMS_SESSION.SET_CONTEXT(CONTEXT_NAME, p_name, p_value);
    ActualValue := SYS_CONTEXT(CONTEXT_NAME, p_name);
    Log('SetParameter', 'READ "' || p_name || '" AS "' || ActualValue || '"');
    Log('SetParameter', 'EXITED');
  END;

  PROCEDURE Initialise IS
    iTmp NUMBER;
  BEGIN
    Log('Initialise', 'ENTERED');
    IF SYS_CONTEXT(CONTEXT_NAME, 'LOWNID') IS NULL THEN
      iTmp := DBMS_RANDOM.RANDOM;
      Log('Initialise', '"LOWNID" has no value, writing "' || iTmp || '"');
      PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
    END IF;
    Log('Initialise', 'EXITED');
  END;

  FUNCTION GetTABLEID RETURN NUMBER IS
    ReadValue VARCHAR2(32767);
  BEGIN
    Log('GetTABLEID', 'ENTERED');
    ReadValue := SYS_CONTEXT(CONTEXT_NAME, 'LOWNID');
    Log('GetTABLEID', 'READ VALUE OF "LOWNID" AS "' || ReadValue || '"');
    Log('GetTABLEID', 'EXITED');
    RETURN TO_NUMBER(ReadValue);
  END;

BEGIN

  Initialise;

END PCKG_TESTGLOBALS;
/

那么解释一下PCKG_TESTGLOBALS中的函数:

日志 - 出于调试原因记录消息。

SetParameter - 获取名称/值对并使用 DBMS_SESSION.SET_CONTEXT 将其存储在 MM_CONTEXT

Initialise - 对于 session 变量LOWNID,它检查变量是否为空,如果是,则使用SetParameter<将其设置为随机值Initialise 在 session 中首次使用包时调用。

GetTABLEID - 这将返回存储在 session 变量 LOWNID 中的值。

最后有一个名为 TR_ONDML_TL_LOWN 的触发器,位于表 LOWN 上 - 其结构在这里并不重要,任何表都可以 - 并在任何DML、插入、更新或删除。

CREATE OR REPLACE TRIGGER TR_ONDML_TL_LOWN
  AFTER INSERT OR UPDATE OR DELETE ON LOWN
DECLARE
  iTmp NUMBER;
BEGIN
  PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'ENTERED');
  iTmp := PCKG_TESTGLOBALS.GetTABLEID;
  PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Read Value "' || iTmp || '"');
  iTmp := NVL(iTmp, 1) + 1;
  PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'Updated Value "' || iTmp || '"');
  PCKG_TESTGLOBALS.SetParameter('LOWNID', iTmp);
  PCKG_TESTGLOBALS.Log('TR_ONDML_TL_LOWN', 'EXITED');
END TR_ONDML_TL_LOWN;
/

这个触发器的目的是这样的:每当表 LOWN 更新时, session 变量 LOWNID 的值就会被读回,并加 1,并被写回。

如果在新 session 中我对 LOWN 表进行了几次连续更新,我会在调试表中得到这些结果 (SELECT MESSAGE FROM DATALOG ORDER BY DATALOG_SEQ)

Initialise:ENTERED
Initialise:"LOWNID" has no value, writing "805223597"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "805223597"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
Initialise:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED
TR_ONDML_TL_LOWN:ENTERED
GetTABLEID:ENTERED
GetTABLEID:READ VALUE OF "LOWNID" AS ""
GetTABLEID:EXITED
TR_ONDML_TL_LOWN:Read Value ""
TR_ONDML_TL_LOWN:Updated Value "2"
SetParameter:ENTERED
SetParameter:SETTING "LOWNID" TO "2"
SetParameter:READ "LOWNID" AS ""
SetParameter:EXITED
TR_ONDML_TL_LOWN:EXITED

从示例中可以看到,它正确地将值传递给DBMS_SESSION.SET_CONTEXT,但它似乎根本没有存储该值。我究竟做错了什么?谢谢。

最佳答案

对于要查看全局可访问上下文的值的 session ,其 client_identifier (根据 SYS_CONTEXT('userenv','client_identifier') )必须匹配 client_id调用 DBMS_SESSION.set_context 时使用的参数.

如果您的set_context调用未设置client_id默认为 NULL;在这种情况下, session 只会看到新值 client_identifier也是 NULL。

如果 session 采用 client_identifier 的任何特定值,您必须在调用 set_context 时使用相同的值.

就您而言,您需要一个所有 session 都可以访问的全局变量;如果您的 session 获得 client_identifier 的随机值,您可能需要在运行代码之前将其设置为 NULL,然后(也许)在将控制权返回给调用者之前恢复其值。

关于Oracle DBMS_SESSION SET_CONTEXT 不存储值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27942040/

相关文章:

sql - 在 Oracle 中按名称验证星期几

javascript - Express 4 - 根据管理员请求销毁所有 session

c++ - 内联成员函数使用的静态全局变量

objective-c - 如何检查当前加载的 View Controller ? (ObjC + swift )

python - 将 Python 函数范围限制为仅局部变量

java - 如果连接列包含有条件的数据,如何在 JPA 中检查

oracle - Oracle SQL Developer 是否与 sqlnet.ora 一起使用?

sql - Oracle/PostgreSQL 中的序列在插入语句中没有 ID

linux - 你可以在 linux 中通过命令重命名 shell session 吗?

php - 显示最近从 $_FILES 上传的文件