包的定义
在PL/SQL程序开发中,为了方便实现模块化程序的管理,可以将PL/SQL元素(如存储过程、函数、变量、常量、自定义数据类型、游标等)根据模块的程序结构组织在一起,存放在一个包中,称为一个完整的单元,并在编译之后存储在数据库服务器中,作为一种全局结构,供应用程序调用。
在Oracle数据库中,包有两类,一类是系统内置的包,每个包实现特定的应用过程、函数、常量等的集合,如DBMS_OUTPUT.put_line()就是调用了DBMS_OUTPUT包中的put_line()函数;另一类是根据应用需要由用户创建的包。
包的创建和调用
包由包规范(specification)和包体(body)两部分组成,在数据库中独立存储。包的创建包括包规范和包体的创建。
- 创建包规范
包规范是指定义包中可以被外部访问的部分,在包规范中声明的内容可以从应用程序和包的任何地方访问。
CREATE [OR REPLACE] PACKAGE 包名称
IS|AS
元素名称定义(类型、变量、存储过程、函数、游标、异常等)
END [包名称];
- 元素声明的顺序可以是任意的,但必须先声明再使用。
- 过程和函数只声明,不进行具体的实现。
- 创建包体
包体负责包规范中定义的函数或存储过程的具体代码实现,如果在包体中定义了包规范中没有的元素,则此部分元素将被设置为私有访问,只能由同一包中的函数或存储过程使用。此外,只有在包规范已经创建的条件下,才可以创建包体,如果包规范中不包含任何函数或存储过程,则可以不创建包体。
CREATE [OR REPLACE] PACKAGE BODY 包名称
IS|AS
元素结构实现;
END [包名称];
- 包体中的名称应与包规范中的包名称保持一致。
- 包体中存储过程和函数的声明格式必须与包规范中的声明完全一致。
包体实现:
create or replace package body pkg_demo
as
--函数:判断员工编号是否存在
······
······
······
包的调用
begin
pkg_demo.proc_insert_users('2022-1-5','yyyy-mm-dd');
pkg_demo.proc_update_users('2022-10-5','yyyy-mm-dd');
end;
包的初始化
包会在第一次被调用其中子程序时初始化。初始化时,改包将从磁盘中被读入共享池中,并启用调用的子程序编译代码,该系统为该包中定义的所有变量分配内存单元,并在整个会话的持续期间保持。每个会话都有打开包变量的副本,以确保执行同一个包子程序的两个会话使用不同的内存单元。
包的初始化过程中只会在包第一次调用时执行一次,因此也称为一次性过程。包的初始化过程是一个匿名的PL/SQL块,定义在包体结构的最后,以BEGIN开始。
包的初始化的语法:
CREATE [OR REPLACE] PACKAGE BODY 包名称
IS|AS
元素结构实现(类型、变量、存储过程、函数、游标、异常等)
BEGIN
包初始化程序代码;
END [包名称];
在pkg_demo包中添加存储过程
--包的初始化
create or replace package body pkg_demo
as
function func_isuseridexist(p_userid number) return boolean --函数
as
v_id number;
begin
select count(*) into v_id from tb_users where user_id=p_userid; --查询结果的行数
if v_id!=0 then
return true;
else
return false;
end if;
end func_isuseridexist;
procedure proc_insert_user(P_userid number,p_create_date date)
as
p_create_date=sysdate;
begin
if func_isuseridexist(p_userid) then
raise e_myexcep;
end if;
if 1=1
then insert into tb_users(user_id,create_date) values (v_id,p_create_date);
else
raise e_myexcep;
end if;
exception
when e_myexcep then
dbms_output.put_line('用户id已存在或创建日期有误');
end proc_insert_user;
--包的初始化代码
BEGIN
SELECT MIN(create_date) into p_create_date from tb_users;
end pkg_demo;
包的持续性
在包规范中,所有声明的元素都具有全局作用域,元素的值在整个用户会话期间将一直存在,因此在用户会话期间,元素可以在应用程序各个部分的代码中被引用。每个用户会话都会维护属于自己会话的元素的副本,用户之间的元素互不干扰。这种在用户会话期间元素值和状态的持续性被称为包的持续性。包的持续性具体可以体现在包中变量的持续性和游标持续性上。
-
变量的持续性是指,当用户调用包时,系统会为每个调用者创建属于该用户的变量副本,并在用户的整个会话期间持续存在。包变量对当前会话用户是私有的。
-
变量的持续性是指,当用户在会话期间多次调用包中的游标时,游标中的结果集会进行连续的检索,而不是每次都从第一条记录开始检索。
1.变量的持续性
下述示例中两个不同用户同时对一个包变量进行存取操作,通过变量值验证包变量在用户会话期间的持续性。首先创建一个包含变量的包以及用于设置和访问包变量的子程序。
创建包变量及设置和访问包变量的子程序:
--创建包含变量的包
create or replace package pkg_var
as
pkgvar number:=0;
end pkg_var;
--创建设置变量值的存储过程
create or replace procedure proc_set_var(p_var number)
as
begin
pkg_var.pkgvar:=p_var;
end proc_set_var;
----输出:过程已创建
--创建获取包变量值的函数
create or replace function fun_get_var return number
as
begin
return pkg_var.pkgvar;
end fun_get_var;
----输出:函数已创建
述示例演示通过用户 scott 登录 SQL* Plus 设置和访间包变量的结果。
通过 scott 用户访问。
coNN scott / tiger ; --已连接。
sQL > EXECUTE proc_set_var(10);
-- 输出: pL/SQL 过程已成功完成。
SELECТ fun_ get_var FROM dual ;
FUN_GEТ_VAR
-------------
10
由上述结果可以看出,此时包变量的值为10。接下来通过用户 system 启动另一个 sQL * Plus 窗口,此时访问包变量的值仍为初始值0,然后重新设置包变量值为20,如下还示例所示。
【示例】通过 system 用户访问。
CONN system /QSTqst2015;---已连接。
SELECТ scott.fun_get_var FROM dual;
FUN_GET_VAR
EXECUTE scott.proc_set_var(20);
-- 输出:PL/sQL 过程已成功完成。
SELECТ scott.fun_get_var from dual;
FUN_GEТ_VAR
---------------
20
接下来回到 scot 用户下,此时再次查询包变量的值,会发现包变量仍为10,并没有受 system 用户操作的影响,如下述示例所示。
【示例】通过 scott 用户访问。
SELЕСТ fun _ get _ var FRo dual ;
FUN _ GET _ VAR
-----------
10
2.游标的持续性
游标持续性游标持续性是指,当用户在会话期间多次调用包中的游标时,游标中的结果集会进行连续的检索,而不是每次都从第一条记录开始检索。
下述示例演示对包中游标进行多次调用,输出连续的检索结果。
【示例】创建包含游标的包。
CREATE OR REPLACE PACKAGE pkg_cursor
AS
CURSOR cur_emp IS SELECТ * FROM scott.emp ;
PROCEDURE proc_emp_list ;
END pkg_cursor;
--程序包已创建。
CREATE OR REPLACE PACKAGE ВODY pkg _ cursor AS PROCEDURE proc_emp_list AS V_emp scott. emp ROWTYPE;
IF NOT cur_emp%ISOPEN THEN
OPEN cur _ emp;
END IF ;
FETCH cur_emp INTO v_emp ;
DBMS_OUTPUT.PUT_LINE (v_emp.empno ':' v _ emp . ename );
END proc_emp_list;
END pkg _ cursor ;
--输出:程序包体已创建。
【示例】访问包中的游标1。
EXECUTE pkg _ cursor . proc _ emp _list;
PL / sQL 过程已成功完成。
包的串行化
由于在包规范中定义的元素为全局元素,每个会话用户都会维护一个元素空间,因此随着用户数量的增加,内存消耗也将越来越大。为此,把包标志为SERIALIY_REUSABLE,串行化包的状态。串行化包的运行状态将仅在每次数据库调用期间而非整个会话期间保持,从而包状态在每一次数据调用结束后会被重置,重新设置所有全局变量,关闭打开的所有游标。
串行化包的实现方法是在包头和包体中添加PRAGMA SERIALLY_REUSABLE语句。
CREATE OR REPLACE PACKAGE pkg_cursor
as
PRAGMA SERIALLY_REUSABLE;
CURSOR cur_emp is select * from tb_user;
procedure proc_emp_list;
end pkg_cursor;
访问包中的游标2
execute pkg_cursor.proc_emp_list;
--过程已成功完成。
包的管理
- 查看包及其源代码
通过查询数据字典视图user_objects、user_source查看当前用户的所有包规范、包体及其源代码。
select object_type,object_name,status from user_objects
where object_type in ('PACKAGE','PACKAGE BODY');
-
修改包
CREATE OR REPLACE PACKAGE语句也可以用于重建包规范,CREATE OR REPLACE PACKAGE BODY语句也可以用于重建包体,通过这种方式可以在修改包的情况下不用重新分配包的权限。 -
重编译包
包的重新编译包括对包规范和包体整体进行重新编译或者分别进行重新编译。其语法如下:
alter package 包名称 compile;
alter package 包名称 compile specification;
alter package 包名称 compile body;
- COMPILE表示重新编译包规范和包体。
- COMPILE SPECIFICATION 表示重新编译包规范。
- COMPILE BODY表示重新编译包体。
分别重新编译包规范和包体
alter package pkg_demo compile specification;
--程序包已变更
alter package pkg_demo compile bady;
--程序包体已变更
- 删除包
当不需要使用定义的包规范或包时,用户可以使用DROP PACKAGE删除整个包,也可以使用DROP PACKAGE BODY语句只删除包体。
drop package pkg_test;
删除包规范的同时会将其对应的包体一起删除。
系统工具包
在Oracle中,除了可以使用用户创建的包外,还可以利用Oracle系统所提供的开发部进行代码的编写,方便应用程序的开发。下述是几个常用的系统包:
- dbms_output包:是最常用的一个系统包 ,dbms_output.put_line()函数就是其中的一个子程序。
- dbms_alter包:用于数据库报警,允许会话间通信。
- dbms_job包:用于任务调度服务。
- dbms_lob包:用于处理大对象操作。
- dbms_pipe包:用于数据库管道,允许会话间通信。
- dbms_sql包:用于执行动态SQL。
- utl_file包:用于文件的输入输出。
除了utl_file包存储在服务器和客户端外,其他的包均存储在服务器中。