Oracle中的package对象是其他数据库中所不存在的特性之一,这是oracle面向对象编程的一种体现.我们可以像定义面向对象语言的对象一样定义oracle的package.
并为package定义对应的属性(全局变量)全局变量(可以定义在包头,也可以在包体,赋值一定在包体)和方法(function,procedure).
package的全局变量在oracle编程过程中有很多有趣并非常实用的作用.比如,我们可以利用这个特性定义带参数的视图,以增加特定情况下(无法通过普通视图完成数据的筛选过程)程序的可维护性,其中的参数就是全局变量,但是得由于封装的缘故,即使是包头中的全局变量,在数据库级包以外的其他对象不公开。
本文利用几个测试简单描述package的全局变量特性和用法.
1、oracle中package的全局变量的全局性是基于session级别的.在session级别设置,修改,维护全局变量的过程不会传输到其他的session中.
我们可以这样理解,oracle将package封装为一个类.在每个session第一次调用时为这个类创立一个实例.而session结束后自动释放实例资源.
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.
第二步, 在session1中执行单步测试过程P_1并传入两个参数v1=1,v2=1.
第三步,另外开启session,调用过程P2.
第四步, 然后回到session1,将过程P_1剩余的代码执行结束.发现输出结果如下.
G1:1
G2:1
结论1:
由于package的全局变量在数据库层次上并不可见是public的,所以每个session都可以认为是一个被实例化了地package对象.在session级别上对全局变量执行的赋值操作并不会被其他session看到.很好地体现了数据的封装性.
2、引用package中的全局变量,database级包外的其它对象虽然可见,但依然无法使用该变量的值.因为这个值为了进行封装,在database级别并不是公用的.
希望通过下述方式调用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;
设想一下这个视图会返回的结果:
a、在pkg_test数据包外进行调用时,由于每次调用pkg_test.f_1之前都会将g_1初始化为0,所以一定会得到’零’这个结果.
b、但是如果同一session我们在调用Pkg_test数据包的过程中,再执行视图,则可以预先通过给G_1赋值的不同而得到我们希望得到的
结论2:
由于oracle的package并没有显式地创建实例,所以package的全局变量跟其他面向对象语言的类的属性还是有区别的.最显著的区别在于这里的全局变量不能作为属性值在其他procedure或者function中进行调用.如果希望达到这样的效果,可以通过在package中定义function来实现.