Oracle系列之三——开发包
包(Package),顾名思义,用于逻辑组合相关的pl/sql类型。同java一样,我们可以将处理同一业务逻辑的相关代码或全局变量放在一个包中。通过使用pl/sql包,不仅能简化应用设计,提高应用性能,而且还可以实现信息隐藏、子程序重载等功能。我们应该学会以下内容:
①建立包规范和包体
②在包内定义公用组件和私有组件
③使用重载特征
④建立构造过程
⑤使用纯度级别
一建立包
包由包规范(package specification)和包体(Package body)两部分组成。当建立包时,应先建立包规范,然后再建立包体。
-
建立包规范
包规范实际上是包与应用程序之间的接口,它用于定义包的公用组件,包括常量、变量、游标、过程和函数等。在包规范中所定义的公用组件不仅可以在包内引用,而且也可以由其它的子程序引用。
建立包规范时,需要注意,为了实现信息的隐藏,不应该将所有组件全部放在包规范处定义,而应该只定义公用组件。
语法如下:
Create [or replace ] package package_name
Is | as
Public type and item declarations
Subprogram specifications
End package_name;
如:
create or replace package dept_package as
v_deptno dept.deptno%type:=30;
procedure add_dept(deptno dept.deptno%type,
dname dept.dname%type,
loc dept.loc%type);
function get_dept(deptno dept.deptno%type) return varchar2;
end dept_package;
-
建立包体
包体用于实现在包规范中所定义的过程和函数。当建立包体中,还可以在其中建立私有组件(实现信息隐藏),包括变量、常量、过程和函数等,但是所定义的私有组件只能在包内使用,而不能由其它子程序引用。
语法如下:
Create or replace package body package_name
Is | as
Private type and item declarations
Subprogram bodies
End package_name;
我们在这里实现上面创建的包规范。
create or replace package body dept_package as
function validate_deptno(v_deptno dept.deptno%type) return boolean is
v_name varchar2(10);
begin
select dname into v_name from dept where deptno = v_deptno;
return true;
exception
when no_data_found then
return false;
end;
--参数名字必须和包规范中声明的完全一样,数据类型一样oracle无法解析为同一个函数。
procedure ADD_DEPT(v_deptno dept.deptno%type,
v_dname dept.dname%type,
v_loc dept.loc%type) is
begin
if validate_deptno(v_deptno) then
dbms_output.put_line('雇员已存在');
else
insert into dept values(v_deptno, v_dname, v_loc);
end if;
end;
--参数名字必须和包规范中声明的完全一样,数据类型一样oracle无法解析为同一个函数。
function GET_DEPT(v_deptno dept.deptno%type) return varchar2 is
v_name varchar2(10);
begin
if validate_deptno(v_deptno) then
select dname into v_name from dept where deptno = v_deptno;
return v_name;
else
raise_application_error(-20000,'部门号不存在');
end if;
end;
end dept_package;
注意:包规范语句和包体语句一定要分两次执行,不然会报错。也就是包规范创建完成,才能创建包体。
-
调用包
如果在同一包中,则直接写函数名即可;如果在包在调用,则package_name.function_name();如果用其它成员调用,则为username_package_name_function_name();
如果调用远程数据库中的包则为:package_name.function_name@db_link();
4.删除包
如果只是删除包体,可以用drop package body package_name;
如果同时删除包规范和包体,则为drop package package_name;
二,使用包重载
重载(overload)是指多个具有相同名称的子程序。定义包时,使用重载特性,可以使用户在调用相同名称的组件时可以使用不同参数传递数据。比如,当查询员工工资时,可能希望输入员工工号,也可能是员工姓名,此时就需要使用包的重载特性。
举例如下:
包规范:
create or replace package body sal_package as
function get_sal(v_empno emp.empno%type) return number;
function get_sal(v_name emp.ename%type) return number;
end sal_package;
包体如下:
create or replace package body sal_package as
function get_sal(v_empno emp.empno%type) return number is
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno = v_empno;
return v_sal;
exception
when no_data_found then
dbms_output.put_line('该雇员号不存在');
return null;
end;
function get_sal(v_name emp.ename%type) return number is
v_sal emp.sal%type;
begin
select sal into v_sal from emp where ename = v_name;
return v_sal;
exception
when no_data_found then
dbms_output.put_line('该雇员名不存在');
return null;
end;
end sal_package;
三.使用包构造过程
在包中定义了全局变量以后,有些情况下,会话中可能还需要初始化全局变量,此时可以使用包的构造函数,类似于高级语言中的构造函数。当在会话第一次调用包的公用组件时,会自动执行其构造函数,并且该构造过程在同一会话内只会执行一次。
下面来说明使用包构造过程的方法:
在包规范中,没有有关包构造过程的任何信息,如下:
create or replace package sal_update_package as
minsal number(6,2);
maxsal number(6,2);
procedure update_sal(v_eno emp.empno%type, v_sal emp.sal%type);
end sal_update_package;
包体。包的构造过程没有任何名称,它只是在其它过程实现完成之后,以
Begin开始、以end结束的部分,如下:
create or replace package body sal_update_package as
procedure update_sal(v_eno emp.empno%type, v_sal emp.sal%type) is
begin
if v_sal between minsal and maxsal then
update emp set emp.sal = v_sal where emp.empno = v_eno;
if sql%notfound then
raise_application_error(-20003,'该雇员不存在');
end if;
else
raise_application_error(-20003,'工资不在范围内');
end if;
end;
begin
select max(sal), min(sal) into maxsal, minsal from emp;
end;
--一定要注意,这里没有end sal_update_package;
当在同一会话中第一次调用包的公用组件进,会自动执行其构造过程,而将来再调用其它组件时不会再调用其构造过程,所以构造过程是"只调用一次"的过程。
四.使用纯度级别
当使用包的公用函数时,它可以作为表达式的一部分,也可以作为SQL语句中使用.但如果在sql语句中引用包的公用函数,那么该公用函数不能包含DML语句(insert,update,delete),也不能读写远程包的变量。为了对包的公用函数加以限制,在定义包规范时可以使用纯度级别(Purity level)限制包的公有函数。定义纯度级别的语句如下:
Pragma restrict_references (function_name,wnds[,wnps][,rnds][,rnps]);
Pragma:表示这是一个编译指令,在编译的时候执行。
WNDS用于限制函数不能修改数据库数据,即不允许DML操作;WNPS用于限制不能修改包变量,即不能给包变量赋值;RNDS用于限制公用函数不能读取数据库数据,即不能用SELECT操作;RNPS用于限制不能读取包变量,即不能变包变量赋值给其它变量。
下面已不能修改包变量为例。
建立包规范:
create or replace package purity_demo as
maxsal number(6,2);
minsal number(6,2);
function get_maxsal return number;
function get_minsal return number;
pragma restrict_references(get_maxsal, wnps);
pragma restrict_references(get_minsal, wnps);
end purity_demo;
建立包体:
create or replace package body purity_demo as
function get_maxsal return number is
begin
select max(sal) into maxsal from emp;
return maxsal;
end;
function get_minsal return number is
begin
select min(sal) into minsal from emp;
end;
endpurity_demo;
在包体中函数修改了包变量的值,那么当在编译时,就会报错:
虽然不能修改,但可能读取。下面我们将包体改成如下形式:
create or replace package body purity_demo as
function get_maxsal return number is
begin
return maxsal;
end;
function get_minsal return number is
begin
return minsal;
end;
begin
select max(sal), min(sal) into maxsal, minsal from emp;
end;
本文档是在学习《Oracle.10g.PL_SQL编程》一书时的记录文档。若喜欢,请购买正版书籍。
参考文档:
《Oracle.10g.PL_SQL编程》:王海亮 林立新 于三禄 郑建茹 等编著,第十二章。