摘要:
Oracle中的package对象是其他数据库中所不存在的特性之一,这是oracle面向对象编程的一种体现.我们可以像定义面向对象语言的对象一样定义oracle的package.并为package定义对应的属性(全局变量)和方法(function,procedure).
package的全局变量在oracle编程过程中有很多有趣并非常实用的作用.比如,我们可以利用这个特性定义带参数的视图,以增加特定情况下(无法通过普通视图完成数据的筛选过程)程序的可维护性.
本文利用几个测试简单描述package的全局变量特性和用法.
Oracle官方文档中的相关解释:
in the same session ,
the variable declared in the package header will not be reset ( global in the session level )
in different session ( database wise ) , they are not global.
So usually we need to write some reset variable function in the beginning of the package so that we will not get confused ( in same session)
可以看出, oracle中package的全局变量的全局性是基于session级别的.在session级别设置,修改,维护全局变量的过程不会传输到其他的session中.
我们可以这样理解,oracle将package封装为一个类.在每个session第一次调用时为这个类创立一个实例.而session结束后自动释放实例资源.
对package的全局变量特性测试.
第一步,创建包含全局变量的package.程序包PKG_TEST.
CREATE OR REPLACE PACKAGE PKG_TEST
IS
PROCEDURE P_1(v1 VARCHAR2,v2 VARCHAR2);
PROCEDURE P_2(v1 VARCHAR2,v2 VARCHAR2);
END;
CREATE OR REPLACE PACKAGE BODY PKG_TEST
IS
G_1 VARCHAR2(20);
G_2 VARCHAR2(20);
PROCEDURE P_1(v1 VARCHAR2,v2 VARCHAR2)
IS
BEGIN
G_1 := v1;
G_2 := v2;
dbms_output.put_line('G1:'||G_1);
dbms_output.put_line('G2:'||G_2);
END;
PROCEDURE P_2(v1 VARCHAR2,v2 VARCHAR2)
IS
BEGIN
G_1 := v1;
G_2 := v2;
dbms_output.put_line('G1:'||G_1);
dbms_output.put_line('G2:'||G_2);
END;
BEGIN
G_1 := 0;
G_2 := 0;
END PKG_TEST;
我们在程序包PKG_TEST中定义了两个全局变量G_1和G_2.在每次调用pkg_test时,我们将两个数据的值都初始化为0.另外定义了两个过程P_1和P_2,都是将传入的参数值赋给G_1和G_2.
第二步, 在session1中执行单步测试过程P_1并传入两个参数v1=1,v2=1.
begin
-- Call the procedure
pkg_test.p_1(v1 => 1,
v2 => 1);
end;
单步运行过程到G_2:=V2处,这时两个变量的值都已经被修改为1.
暂停测试P_1的过程.
第三步,另外开启session,调用过程P2.
Begin
PKG_TEST.P_2(2,2);
End;
我们看到最后输出的内容如下.
G1:2
G2:2
第四步, 然后回到session1,将过程P_1剩余的代码执行结束.发现输出结果如下.
G1:1
G2:1
测试意图:
在session1给全局变量赋值之后,使用之前有其他session执行了对变量G_1和G_2的赋值操作.测试其他session对全局变量的修改会不会影响到当前session已经为变量赋的值.
结论1:
由于package的全局变量在数据库层次上并不可见是public的,所以每个session都可以认为是一个被实例化了地package对象.在session级别上对全局变量执行的赋值操作并不会被其他session看到.很好地体现了数据的封装性.
结论2:
由于oracle的package并没有显式地创建实例,所以package的全局变量跟其他面向对象语言的类的属性还是有区别的.最显著的区别在于这里的全局变量不能作为属性值在其他procedure或者function中进行调用.如果希望达到这样的效果,可以通过在package中定义function来实现.
例如希望通过下述方式调用pkg_test的全局变量是错误的.
CREATE OR REPLACE VIEW v_test
AS
SELECT decode(pkg_test.G_1,'0','零','非零') FROM dual;
这时,oracle会提示错误.
PKG_TEST.G_1无效的标识符.这是因为pkg_test的全局变量是在package body中定义的,所以这个全局变量对oracle的其他对象不可见.
如果我们修改pkg_test的定义,将全局变量放到package中进行定义,这样我们在v_test中虽然可以看到g_1,但依然无法使用该变量的值.因为这个值为了进行封装,在database级别并不是公用的.
CREATE OR REPLACE PACKAGE PKG_TEST
IS
G_1 VARCHAR2(20);
G_2 VARCHAR2(20);
PROCEDURE P_1(v1 VARCHAR2,v2 VARCHAR2);
PROCEDURE P_2(v1 VARCHAR2,v2 VARCHAR2);
END;
再次执行刚才的视图定义,看到报错提示为:
ora-06533:PLS-221G_1不是过程或尚未定义.
解决方式如下.
修改pkg_test的定义,为package增加两个函数F_1,F_2,用来返回G_1和G_2的数据,这样的两个函数就可以被其他过程引用了.
CREATE OR REPLACE PACKAGE PKG_TEST
IS
FUNCTION F_1 RETURN VARCHAR2;
FUNCTION F_2 RETURN VARCHAR2;
END;
CREATE OR REPLACE PACKAGE BODY PKG_TEST
IS
G_1 VARCHAR2(20);
G_2 VARCHAR2(20);
FUNCTION F_1
RETURN VARCHAR2
IS
BEGIN
RETURN G_1;
END;
FUNCTION F_2
RETURN VARCHAR2
IS
BEGIN
RETURN G_1;
END;
BEGIN
G_1 := 0;
G_2 := 0;
END PKG_TEST;
这时我们就可以成功执行视图的创建了.
CREATE OR REPLACE VIEW v_test
AS
SELECT decode(pkg_test.F_1,'0','零','非零') F1 FROM dual;
设想一下这个视图会返回的结果:
1,在pkg_test数据包外进行调用时,由于每次调用pkg_test.f_1之前都会将g_1初始化为0,所以一定会得到’零’这个结果.
2,而如果我们在pkg_test数据包的过程中进行调用,则可以预先通过给G_1赋值的不同而得到我们希望得到的结果.