我正在尝试在 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/