什么是包
一个PL/SQL包由两部分组成:
- 包规范:主要是包的一些定义信息,不包含具体的代码实现,是PL/SQL程序的接口部分,包含类型、记录、变量、常量、异常定义、游标和子程序的声明。
- 包体:包体是对包规范中声明的子程序的实现部分,包体的内容对于外部应用程序来说是不可见的。
包的优点
- 模块化设计。
- 规范化的程序设计。
- 实现信息的隐藏。
- 提供全局共享的附加功能:在包中公开的变量或游标在一个会话期会一直存在,并且可以被当前环境下的所有子程序共享,因此可以将包中定义的变量当做全局变量来使用,并且可以跨事务来维护数据而不用把它保存在数据库中。
- 提供了良好的性能体验:由于在首次打开包子程序时,整个包都会被加载到内存中,后续的调用只需要从内存中读取而不需要再次读取磁盘。
定义包规范
代码如下:
--定义包规范,包规范将被用于应用程序的接口部分,供外部调用
CREATE OR REPLACE PACKAGE emp_pkg AS
--定义集合类型
TYPE emp_tab IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
--在包规范中定义一个记录类型
TYPE emprectyp IS RECORD(
emp_no NUMBER,
sal NUMBER
);
--定义一个游标变量
CURSOR desc_salary RETURN emprectyp;
--定义雇佣员工的过程
PROCEDURE hire_employee(p_empno NUMBER,p_ename VARCHAR2,p_job VARCHAR2,p_mgr NUMBER,p_sal NUMBER,
p_comm NUMBER,p_deptno NUMBER,p_hiredate DATE);
--定义解雇员工的过程
PROCEDURE fire_employee(p_emp_id NUMBER );
END emp_pkg;
定义包体
代码如下:
--定义包体
CREATE OR REPLACE PACKAGE BODY emp_pkg
AS
--定义游标变量的具体类型
CURSOR desc_salary RETURN emprectyp IS
SELECT empno, sal FROM emp ORDER BY sal DESC;
--定义雇佣员工的具体实现
PROCEDURE hire_employee(p_empno NUMBER,p_ename VARCHAR2,
p_job VARCHAR2,p_mgr NUMBER,p_sal NUMBER,
p_comm NUMBER,p_deptno NUMBER,p_hiredate DATE) IS
BEGIN
--向emp表中插入一条员工信息
INSERT INTO emp VALUES(p_empno,p_ename,p_job,p_mgr,p_hiredate,p_sal,p_comm,p_deptno);
END;
--定义解雇员工的具体实现
PROCEDURE fire_employee(p_emp_id NUMBER ) IS
BEGIN
--从emp表中删除员工信息
DELETE FROM emp WHERE empno=p_emp_id;
END;
END emp_pkg;
调用包组件
当包被第一次调用时,将进行初始化,比如将包从硬盘上调到内存中来,放到系统全局工作区的共享缓冲池中,包的运行状态则被放入用户全局区的会话存储区中。因此可以保证每个调用报的会话都拥有包的运行副本,当会话结束时,包的运行状态才会被释放。因此包中的变量具有会话级的作用域,因而可以跨多个事务存储数据。
包的这种特性使得我们可以在包中保存跨多个事务的数据,这大大方便了复杂的程序逻辑的中间数据的存储,而不用特意用一个表来存储临时的数据。
如果在定义包规范时,指定了编译提示SERIALLY_REUSABLE
,则可以将包的运行状态保存在系统全局工作区,而不是用户全局区,这样每次调用包以后,包的运行状态就会被释放,这样再次调用包时,将重新开始包的状态。
PRAGMA SERIALLY_REUSABLE;
不过这种每次调用便释放的连续进行会占用大量的内存,内存的占用量与包的并发调用用户数成正比,而与当前登录的用户数无关,因此需要谨慎使用。
编译和调试包
可以使用PL/SQL Developer的调试功能进行可视化的调试和编译,也可以使用ALTER PACKAGE语句来重新编译包。
ALTER PACKAGE emp_pkg COMPILE BODY; --编译包体
ALTER PACKAGE emp_pkg COMPILE PACKAGE; --编译包规范和包体
ALTER PACKAGE emp_pkg COMPILE SPECIFICATION; --编译包规范
显式地对包进行重新编译可以清楚运行阶段的隐式编译,这样可以防止在运行时的编译错误及所带来的性能开销。
查看包的源代码
与子程序一样,可以查询user_objects
视图来查看数据字典中包含的包的信息。
SELECT object_type, object_name, status
FROM user_objects
WHERE object_type IN ('PACKAGE', 'PACKAGE BODY');
也可以从user_source
视图中获取包的源代码,
SELECT line, text
FROM user_source
WHERE NAME = 'EMP_ACTION_PKG' AND TYPE = 'PACKAGE'
ORDER BY line;
SELECT line, text
FROM user_source
WHERE NAME = 'EMP_ACTION_PKG' AND TYPE = 'PACKAGE BODY'
ORDER BY line;