果冻想
认真玩技术的地方

Oracle学习笔记——包

Oracle中的面向对象

写惯了C++/Java代码,心里就想着什么都是面向对象,就想着封装。当在工作中写多了存储过程,函数的时候,发现你写的东西都是分散在系统中的,并没有按照面向对象的思想组织起来,写的多了,发现自己写的那些存储过程,函数都不好管理。后来查资料,发现Oracle中有“包”这么个概念,具有面向对象的“味道”,所以就找时间看了看Oracle中的包,总结成一篇文章,也是自己学习成果的总结。

说说包

包也是模式对象,它是将相关元素集成在一起的PL/SQL结构。包由以下两部分组成:

  • 包头
  • 包体

包头和包体是单独存放在数据库的字典中。在包中,我们可以集成以下对象:

  • 存储过程
  • 函数
  • 游标
  • 变量
  • 常量
  • 异常
  • 记录类型

像中国移动这种大型的电信公司,其业务复杂程度不是你可以想象的,每一个功能模块都是由N个存储过程、函数等组成的,而这些过程、函数默认是放在一起的(如在PL/SQL Developer中,过程默认都是放在一起的,即Procedures中),这些非常不方便查询和维护,甚至会发生误删除的事件。如果将这些存储过程、函数等按照功能模块进行划分,分别封装在不同的包中,那后期对这些功能模块进行维护时,模块之间将会变得异常清晰,极大的降低维护成本。先来创建一个包吧。

包头和包体

上面说了包由包头和包体两部分组成,包头就好比类的声明文件,包体就好比类的定义文件。

  • 包头——包的说明部分
    包头定义包的调用接口,列出了包中包含的对象。外部程序可以调用这些对象。

  • 包体——包的主体
    包头只是定义包中对象的调用接口,而这些对象的真正实现代码则被放在包体中。也就是说如果你在包头部分定义了游标,在包体中则要给游标指定select语句。如果包头定义了存储过程或者函数,则要在包体中编写实现这些存储过程或者函数的代码。

对于高级语言中的权限控制,在Oracle的包中也存在这样的概念。在包头中定义的对象,包外面的程序可以对其进行引用;当然了,包内的存储过程、函数等也可以对其进行引用;在包体中定义的对象只能在包体中进行引用,包外的对象不能对它们进行引用。这就是Oralce包中最简单的访问权限控制。

创建一个包

包是由包头和包体两部分创建的,所以在创建包的过程中也分为以下两部分:

  • 创建包的声明部分——包头
  • 创建包的定义部分——包体

创建一个包头:

create or replace package pack_test as
    b varchar2(10); -- 定义变量
    c_id constant number := 100; -- 定义常量
    type employ_info is record -- 定义类型
    (
        employ_id varchar2(10),
        salary number(20, 2)
    );
    cursor employ_cur return employ_info; -- 定义游标变量
    invalid_salary exception; -- 定义异常
    function hire_employee(name in varchar2, age number, sex varchar2) return int;
    procedure fire_employee(emp_id varchar2); -- 定义存储过程
end;

可以看到,我在包头中指定定义了变量、函数和存储过程,并没有实现函数和存储过程。在包体中我们将对定义的游标、函数和存储过程进行编码实现。接下来看看包体的实现。

 create or replace package body pack_test as
    cursor employ_cur return employ_info
        is select emp_id, salary from employee;

    default_salary constant int := 5000; -- 包体中定义的变量,相当于私有变量,包外无法引用
    total_count int;

    -- 定义函数
    function hire_employee(name in varchar2, age number, sex varchar2) return int is
        str constant varchar2(50) := 'Call Function';
    begin
        dbms_output.put_line(str);
        dbms_output.put_line('Name is :' || name || '; Age is :' || age || '; Sex is :' || sex);
        return 1;
    end hire_employee;

    -- 定义存储过程
    procedure fire_employee(emp_id varchar2) is
        str constant varchar2(50) := 'Call procedure';
    begin
        dbms_output.put_line(str);
        dbms_output.put_line('Employee ID is :' || emp_id);
    end fire_employee;

begin
    -- 这里可以对一些变量进行初始化
    -- 这些变量的初始化发生在第一次引用包中对象时
    total_count := 0;
end pack_test;

上面的包体就是把包头中的定义进行了简单的实现;但是有一点需要特别注意,就是包体的包名和包头的包名必须一致,否则就不发找到包头对应的包体定义了。关于其它的就没有多少好说的了,只要看过之前的文章,上述的代码就轻车熟路了,你只需要掌握定义包的语法格式即可。

编译包

定义完包以后,就需要对其进行编译,将其保存到数据字典中。由于包由包头和包体两部分组成,我们既可以对整个包进行整体编译,也可以单独对包头或者包体进行编译。

  • 对整个包进行编译
    alter package pack_test compile package;
    
  • 对包头进行编译
    alter package pack_test compile specification;
    
  • 对包体进行编译
    alter package pack_test compile body;
    

当你在编译包的时候,可以使用以下语句查看具体的错误:

show errors package <包名>;
show errors package body <包名>;

调用包中元素

好了,包头和包体都已经编写完毕,也已经正确的编译通过了,接下来就轮到我们去调用这个包中的内容了。调用包中的函数或存储过程也分为以下两种情况:

  • SQL命令直接调用
  • PL/SQL中调用

当使用SQL命令直接调用包中对象时,需要使用execute命令,例如:

execute pack_test.fire_employee('3008732');

在PL/SQL中调用包中对象,直接调用即可,例如:

-- Created on 9/9/2015 by JELLY 
declare 
    -- Local variables here
    i integer;
begin
    -- Test statements here
    i := pack_test.hire_employee('Jelly', 23, 'M');
    dbms_output.put_line(i);

    pack_test.fire_employee('660045214');
end;

可以看到,调用包中的函数或存储过程就像调用其它的函数和存储过程一样,只是多了一个包名限定。

删除包

当你觉的这个包有点多余了,你完全可以删除它。由于包是由包头和包体两部分组成的,所以删除包的时候,多多少少和删除函数或存储过程是有点区别的。

drop package body pack_test;

这样就删除了包体。而如果需要删除整个包,则需要:

drop package pack_test;

当然了,执行这一切的前提是你必须具有drop any procedure权限。

重载包中存储过程和函数

简单说一下Oracle中重载的概念。

多个具有相同名字的函数或存储过程,但是它们的参数个数或参数类型不同。当调用这样的函数或存储过程时,即使存储过程或者函数的名字相同,根据不同的参数个数或参数类型,Oracle会自动调用相应的存储过程或者函数。

这样的概念和C++中的重载是一个意思。当我看到Oracle包中居然支持重载时,我是很吃惊的。是的,它支持重载,虽然仅仅只在包中支持。对于Oracle中重载有几点需要注意。

  • 重载的概念交代的很清楚,参数类型或个数不一样
  • 如果参数仅仅是模式的不同,不能称之为重载,例如:
    procedure proc_1(param in number);
    procedure proc_1(param out number);
    

    亲爱的,这不是重载。

  • 函数返回值的类型不同也不能算重载,同其它高级语言

  • 同族的参数类型,也不能算作重载,例如:

    procedure proc_1(p in char);
    procedure proc_1(p in varchar2);
    

    由于char和varchar2同属于字符型的参数,这样也不能算作重载。

总结

Oracle中的包看起来不错,我好喜欢。在做下一个功能的时候,看看能不能完美的使用以下包。学习就是这样,学了就要去用,不用的话,你永远不知道你不会。

2015年9月9日 于呼和浩特。

未经允许不得转载:果冻想 » Oracle学习笔记——包
网站维护离不开您的支持,您可以赞助本站,谢谢支持
×

感谢您的支持,我们会一直保持!

扫码支持
请土豪扫码随意打赏

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

赞助本站
关注微信公众号
关注微信公众号和果冻一起分享你的疑惑与心得。
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

玩技术,我们是认真的

联系我们关于果冻