程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle數據庫基礎 >> Oracle 包 學習筆記

Oracle 包 學習筆記

編輯: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中的包看起來不錯,我好喜歡。在做下一個功能的時候,看看能不能完美的使用以下包。學習就是這樣,學了就要去用,不用的話,你永遠不知道你不會。

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved