PLSQL面向对象编程

面向对象编程简称OOP编程,实际上是对现实世界事物的一种抽象的过程。它的核心是把对象的定义和实现进行区分,让定义部分对象所具有的结构,让实现部分根据定义部分定义的具体结构进行具体的实现。


用于生产玩具的模具叫做类,通常设计类的过程也可以称为建模,当然这个模不是模具的模,而是对类的模型进行建模。所生产的玩具可以叫做对象,类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模版。


对类的定义是一组具有相同数据结构和相同操作的对象的集合,类的定义包括一组代表其特性的属性。以及一组表示其执行行为的方法,类定义可以看作是一个具有相似特性与共同行为的对象模版,可以用来产生对象,而每个对象都是类的实例,都可以使用类中提供的方法,对象的具体状态包含在对象实例变量中。


封装:也叫做信息封装,确保对象不会以不可预期的方式改变其他对象的内部状态。只有在那些提供了内部状态改变方法的对象中,才可以访问其他内部状态。每类对象都提供一个与其他对象联系的接口,并规定了其他对象进行调用的方法


多态性:对象的引用和类会涉及其他许多不同类型的对象,而且引用对象所产生的结果将依据实际调用的类型


继承性:允许在现存的对象基础上创建子类对象,统一并增强了多态性和封装性。典型地来说就是用类来对对象进行分组,而且还可以定义新类为现存的类的扩展,这样就可以将类组织成树型或网状结构,这体现了对象的通用性。


对象类型实际上就是在上一小节中介绍的类型,类的实例就是对象。对象的类型封装了数据结构和用于操纵这些数据结构的过程和函数,这使得通过定义对象类型就可以封装一些较复杂的代码,提高应用开发的效率和速度。可以在一个对象类型说明部分声明属性和方法,属性不能是常数,异常,游标或类型,最大的属性声明数量是1000个,但是必须至少声明一个属性,而方法是可选的。属性描述了对象的特性,方法则是对象类型具有的功能。在PLSQL中,方法就是一些子程序,可以是函数,也可以是过程,方法名称不能和它的对象类型名称和属性名称一样,方法在实例级别或对象类型级别被调用。



PLSQL中对象的组成结构


PLSQL中的对象类型是一种自定义的符合类型,它的定义与包的定义非常相似

对象类型规范:是对象与应用的接口,它用于定义对象的公用属性和方法

对象类型体:用于实现对象类型规范所定义的公用方法.


例如在定义员工对象类型时,先在对象类型规范中定义好了对象的所有属性,以及对象可被调用的方法声明,这些方法并没有具体的实现部分,仅可供外部调用的方法签名。而具体的方法体代码实现则定义在对象类型体中。


在定义对象类型的属性时,不能指定对象属性的默认值,也不能指定NOT NULL选项。


PLSQL中可以定义的几种类型的方法

1.构造方法:该方法类似于JAVA等语言中的构造函数,用来初始化一个对象类型并返回对象的实例

2.MEMBER方法:该方法允许对象的实例进行调用,在MEMBER方法中可以访问对象实例的数据,通常称为实例方法或成员方法

3.STATIC方法:该方法可以直接在对象类型上进行调用,它用于在对象类型上执行全局操作,通常称为静态方法

4.MAP方法:用于在多个对象间排序的映射方法。

5.ORDER方法:用于在两个对象实例间排序的排序方法。



定义对象类型

对象类型包含对象类型规范和对象类型体两大部分,因此在定义时必须先定义对象类型规范,然后再定义对象类型体.


定义employee_obj对象规范

create or replace type employee_obj as object(

empno number(4),

ename varchar2(20),

job varchar2(20),

sal number(10,2),

comm number(10,2),

deptno number(4),

MEMBER procedure change_sal(p_empno number,p_sal number),

MEMBER procedure change_comm(p_empno number,p_comm number),

MEMBER procedure change_deptno(p_empno number,p_deptno number),

MEMBER procedure get_sal(p_empno number) return number,

MEMBER procedure get_comm(p_empno number) return number,

MEMBER procedure get_deptno(p_empno number) return integer

)NOT FINAL;  --指定该类可以被继承,如果指定FINAL,表示该类无法被继承


定义对象体

create or replace type body employee_obj

as

member procedure chang_sal(p_empno number,p_sal number)

is

begin

update emp set sal=p_sal where empno=p_empno;

end;

member procedure chang_comm(p_empno number,p_sal number)

is

begin

update emp set comm=p_comm where empno=p_empno;

end;

member procedure chang_deptno(p_empno number,p_sal number)

is

begin

update emp set deptno=p_deptno where empno=p_empno;

end;

member function get_sal (p_empno number)

return number

is

v_sal number(10,2);

begin

select sal into v_sal from emp where empno=p_empno;

return v_sal;

end;

member function get_comm(p_empno number)

return number

is

v_comm number(10,2);

begin

select comm into v_comm from emp where empno=p_empno;

RETURN v_comm;

end;

member function get_deptno(p_empno number)

return integer

is

v_deptno int;

begin

select deptno into v_deptno from emp where empno=p_empno;

return v_deptno;

end;

end;



定义属性

属性是对象类型特性的定义,属性声明是一个对象必需的,也就是说一个对象类型至少要定义一个属性。属性的定义与变量的定义相似,也具有名称和数据类型,在整个对象类型中,属性的名称必须是唯一的,但是在不同的对象类型之间,属性的命名是可以重复的。


1.属性的声明必须在方法的声明以前,也就是说在对象规范中create type下面的声明必须最先是属性的定义。

2.属性的数据类型必须是oracle数据库类型,不能是任何PLSQL类型或PLSQL自定义类型,但是排除了oracle中的rowid,urowid,long,log raw,nchar,nclob,nvarchar2类型。

3.在定义属性时不能对属性应用NOT NULL约束或使用default指定默认值。

4.在一个对象类型中至少要定义一个属性,但是不能大于1000个属性.

属性的类型既可以是简单的数据类型,也可以是对象类型的引用.


定义对象的属性

create or replace type employee as object(

empno number(4),

ename varchar2(20),

job   varchar2(20),

sal   number(10,2),

comm  number(10,2),

deptno number(4)

)NOT FINAL;


在定义了该对象类型之后,就可以在PLSQL语句块中通过实例化对象类型,读取或写入对象的属性值了.

declare

v_emp  employee_property;

v_sal  v_emp.sal%TYPE;

begin

v_emp:=employee_property(7890,'赵五','销售人员',5000,200,20);

v_sal:=v_emp.sal;

dbms_output.put_line(v_emp.ename||'的薪资是:'|| v_sal);

end;




定义方法

对象方法是在对象规范定义中使用MEMBER或STATIC声明在对象说明部分的子程序,它们是在属性声明之后进行的,

MEMBER方法:成员方法是基于对象实例而不是基于对象类型调用的

STATIC方法:静态方法独立于对象实例,也不能在对象类型主体中引用这个对象的属性


使用MEMBER和STATIC成员方法

create or replace type employee_method as object(

empno number(4),

sal number(10,2),

comm number(10,2),

deptno number(4),

MEMBER procedure change_sal, --实例方法,可以访问对象本身的属性

MEMBER function get_sal return number,

--静态方法,不能访问对象本身的属性,只能访问静态数据

STATIC procedure change_deptno(p_empno number,p_deptno number),

STATIC function get_sal(p_empno number) return number


)NOT FINAL;   --指定该类可以被继承,如果指定FINAL,表示该类无法被继承


create or replace type body employee_method

as

MEMBER procedure change_sal

is

begin

self.sal:=self.sal*1.12;

end;

MEMBER function get_sal

return number

is

begin

return sal;

end;

STATIC procedure change_deptno(p_empno number,p_deptno number)

is

begin

update emp set deptno=p_deptno where empno=p_empno;

end;

STATIC function get_sal(p_empno number)

return number

is

v_sal number(10,2);

begin

select sal into v_sal from emp where empno=p_empno;

return v_sal;

end;

end;


MEMBER和STATIC方法使用示例

declare

v_emp employee_method;

begin

v_emp:=employee_method(7999,5000,200,20); --实例化employee_method对象,现在v_emp是对象实例

v_emp.change_sal; --调用对象实例方法,即MEMBER方法

dbms_output.put_line('员工编号为:'||v_emp.empno||'的薪资为:'||v_emp.get_sal);

--下面的代码调用STATIC方法更新emp表中员工编号为7369的部门编号为20

employee_method.change_deptno(7369,20);

--下面的代码获取emp表中员工编号为7369的员工薪资

dbms_output.put_line('员工编号为7369的薪资为:'||employee_method.get_sal(7369));

end;



使用SELF关键字

每一个MEMBER类型方法都隐式地声明了一个内联参数SELF,它代表了对象类型的一个实例,总是被传递给成员方法的第一个参数,实际上的方法体内,也可以不用SELF。


访问对象类型的属性

create or replace type employee_salobj as object(

empno number(4),

sal number(10,2),

comm number(10,2),

deptno number(4),

MEMBER procedure change_sal,

MEMBER procedure change_comm,

MEMBER procedure change_deptno,

MEMBER function get_sal return number,

MEMBER function get_comm return number,

MEMBER function get_deptno return INTEGER

)NOT FINAL;



create or replace type body employee_salobj

as

MEMBER procedure change_sal

is

begin

self.sal:=self.sal*1.12;

end;

MEMBER procedure change_comm

is

begin

comm:=comm * 1.12;

end;

MEMBER procedure change_deptno

is

begin

self.deptno:=20;

end;

MEMBER function get_sal

return number

is

begin

return sal;

end;

MEMBER function get_comm

return self.comm;

end;

MEMBER function get_deptno

return integer

is

begin

return self.deptno

end;

end;

在employee_salobj对象类型体的成员实现中,显式地使用self进行对象属性的访问,当没有显式使用self关键字时,实际上也是隐式地使用了这个关键字。

由于STATIC属于静态方法级别,因此它不能接受或引用self关键字



定义构造函数


当定义了一个对象类型之后,系统会提供一个接收与每个属性相对应的参数的构造函数。

一般出于如下目的来自定义构造函数:

1.为对象提供初始化功能,以避免许多具有特别用途的过程只初始化对象的不同部分,可以通过构造函数进行统一初始化

2.可以在构造函数中为某些属性提供默认值,这样就能确保属性值的正确性,而不必依赖于调用者所提供的每一个属性值。

3.出于维护性的思考,在新的属性添加到对象中时,避免要更改调用构造函数的应用程序中的代码,这样可以使已经存在的构造函数调用继续工作.


构造函数的定义是一个与对象类型名称具有相同名称的函数,用于初始化对象,并能返回一个对象类型的新实例。在自定义构造函数时,要么就覆盖由oracle为每一个对象生成的默认构造函数,要么就定义一个有着不同方法签名的新构造函数。自定义构造函数使用constructor关键字进行声明.


自定义构造函数示例

create or replace type salary_obj as object(

percent number(10,4),  --定义对象类型

sal number(10,2),

--自定义构造函数

constructor function salary_obj(p_sal number) return self as result)

instantiable

final;

/


--定义对象类型体

create or replace type body salary_obj

as

--实现重载的构造函数

constructor function salary_obj(p_sal number)

return self as result

as

begin

self.sal:=p_sal;

self.percent:=1.12;

return;

end;

end;

/


调用

declare

v_salobj1 salary_obj;

v_salobj2 salary_obj;

begin

v_salobj1:=salary_obj(1.12,3000);   --使用默认构造函数

v_salobj2:=salary_obj(2000); --使用自定义构造函数

end;




定义MAP和ORDER方法

MAP方法:该函数会将对象实例根据一定的调用规则返回FATE,NUMBER,varchar2类型的标量类型,在映射对象类型为标量函数后,就可以通过对标量函数的比较来得到结果了。

ORDER方法:order方法只能对两个对象之间进行比较,必须是返回数值型结果的函数,根据结果返回正数,负数或零。该方法只有两个参数,SELF和另外一个要比较的对象类型,如果传递该参数为NULL,则返回NULL。


由于MAP方法一次调用时就将所有的对象映射为一个标量值,因此通常用在排序或合并很多对象时。而order方法一次仅能比较两个对象,因此在比较多个对象时需要被重复调用,效率会低一些。


定义MAP函数示例

create or replace type employee_map as object(

empno number(4),

sal number(10,2),

comm number(10,2),

deptno number(4),

MAP MEMBER FUNCTION convert return real     --定义一个MAP方法

)NOT FINAL;


create or replace type body employee_emp as

MAP MEMBER FUNCTION convert return real is     --定义一个MAP方法

begin

return sal+comm;

end;

end;


在定义了MAP函数后,PLSQL会隐式地通过调用MAP函数在多个对象间进行排序或比较。例如下面创建了一个emp_map_tab的对象表,向这个对象表插入多个对象,然后就可以对这个对象表进行对象的排序

create table emp_map_tab of employee_map;

insert into emp_map_tab values(7123,3000,200,20);


col val format a60;

select value(r) val,r.sal+r.comm from emp_emp_tab r order by 1;




定义order函数示例

create or replace type employee_order as object(

empno number(4),

sal number(10,2),

comm number(10,2),

deptno number(4),

ORDER MEMBER FUNCTION match(r employee_order) return integer --定义一个ORDER方法

)NOT FINAL;


create or replace type body employee_order as

ORDER MEMBER FUNCTION match(r employee_order) return integer is

begin

if ((SELF.sal+SELF.comm)<(r.sal+r.comm)) then

return -1;

elsif((SELF.sal+SELF.comm)>(r.sal+r.comm)) then

return 1;

else

return 0;

end if;

end match;

end;


定义了order函数后,就可以对两个对象进行比较。

declare

emp1 employee_order:=employee_order(7112,3000,200,20);

emp2 employee_order:=employee_order(7113,3800,100,20);

begin

if emp1>emp2 then

dbms_output.put_line('员工1的薪资加提成比员工2大');

elsif emp1<emp2 then

dbms_output.put_line('员工1的薪资加提成比员工2小');

else

dbms_output.put_line('员工1的薪资加提成与员工2相等');

end if;

end;




使用对象类型


对象类型一旦被创建成功,就被保存到了oracle数据字典中,用户可以再任何的PLSQL块,子程序或包中使用它来声明对象

1.声明对象

声明的对象必须是已经在oracle数据字典中存在的对象类型。在声明时可以直接使用对象的构造函数进行初始化,如果没有初始化,那么对象实例初始化为NULL。


declare

o_emp employee_order;

begin

o_emp:=employee_order(7123,3000,200,20);

dbms_output.put_line('员工编号为:'

||o_emp.empno

||'的薪资和提成为:'

||(o_emp.sal+o_emp.comm)

);

end;


在语句块的定义区定义了对象类型的变量,o_emp在定义区的初始化状态下为NULL,在语句块的执行部分,使用对象类型的构造函数实例化了对象类型,然后通过访问对象类型的属性来获取对象实例的信息.


也可以将对象类型作为存储过程和存储函数的形式参数,将对象实例从一个子程序传递到另一个子程序。


在子程序中使用对象类型

create or replace procedure changesalary(p_emp in employee_order)

as

begin

if p_emp is not null then         --如果对象类型已经实例化,更新emp表

update emp set sal=p_emp.sal,comm=p_emp.comm where empno=p_emp.empno;

end if;

end changesalary;


--使用对象类型作为函数的传出参数

create or replace function getsalary(p_emp in out employee_order) return number

as

begin

if p_emp is not null then          --如果没有实例化,那就实例化对象类型

p_emp:=employee_order(7125,5000,800,20);

end if;

return p_emp.sal+p_emp.comm;

end;


2.初始化对象

declare

o_emp employee_order:=employee_order(NULL,NULL,NULL,NULL);

begin

o_emp.empno:=7301;

o_emp.sal:=5000;

o_emp.comm:=300;

o_emp.deptno:=20;

end;


3.调用对象方法

在对象类型中,方法分为静态方法和实例方法,静态方法在定义时使用STATIC前缀,实例方法在定义时使用MEMBER关键字进行定义。这两类方法的调用方式也不同,实例方法在实例级别进行调用,而静态方法在对象类型的级别,也就是类级别进行调用。在一些高级程序设计语言比如JAVA,静态方法又称为类方法。


调用静态方法与实例方法实例

create or replace type employee_method as object(

empno number(4),

sal number(10,2),

comm number(10,2),

deptno number(4),

MEMBER PROCEDURE change_sal,

MEMBER FUNCTION get_sal return number,

STATIC PROCEDURE change_deptno(empno number,deptno number),

STATIC FUNCTION get_sal(empno number) return number

)not final;


调用

declare

o_emp employee_method:=employee_method(7369,5000,800,20);

v_sal number(10,2);

begin

v_sal:=o_emp.get_sal;           --调用对象实例级别的方法

dbms_output.put_line('对象实例级别的工资为:'||v_sal);

v_sal:=employee_method.get_sal(o_emp.empno);   --调用对象级别的方法

dbms_output.put_line('对象类型级别的工资为:'||v_sal);

end;


使用嵌套对象类型

嵌套对象类型是指一个对象中嵌入另一个对象类型。在为对象类型定义属性时,除了可以使用标量类型之外,还可以通过自定义的对象类型来提升整个对象类型的灵活性。


定义地址对象类型

create or replace type address_type

as object

(street_addr1 varchar2(25),

street_addr2 varchar2(25),

city varchar2(30),

state varchar2(2),

zip_code number,

--成员方法,返回地址字符串

MEMBER FUNCTION tostring return varchar2,

--MAP方法提供地址比较函数

MAP MEMBER FUNCTION mapping_function return varchar2

)


--定义地址对象类型体,实现成员方法与MAP函数

create or replace type body address_type

as

MEMBER FUNCTION tostring

return varchar2

is

begin

if (street_addr2 is not null)

then

return street_addr1

||CHR(10)

||street_addr2

||CHR(10)

||city

||','

||state

||' '

||zip_code;

else

return street_addr1 ||CHR(10)||city||','||state||' '||zip_code;

end if;

end;

MAP MEMBER FUNCTION mapping_function

return varchar2

is

begin

return to_char(NVL(zip_code,0),'fm00000')

||LPAD (NVL(city,''),30)

||LPAD (NVL(street_addr1,''),25)

||LPAD (NVL(street_addr2,''),25);

end;

end;


定义包含其他对象类型的类型

create or replace type employee_addr as object(

empno number(4),

sal number(10,2),

comm number(10,2),

deptno number(4),

addr address_type,

MEMBER FUNCTION get_emp_info return varchar2

)NOT FINAL;


--定义对象类型体,实现get_emp_info方法

create or replace type body employee_addr

as

MEMBER FUNCTION get_emp_info

return varchar2

is

begin

return '员工'||SELF.empno||'的地址为:'||SELF.addr.tostring;

end;

end;


为了使用employee_addr对象类型,在构造对象的实例时,必须要同时构造address_type的嵌套对象类型。

declare

o_address address_type;

o_emp employee_addr;

begin

--实例化地址对象类型

o_address:=address_type('玉兰一街','二巷','深圳','DG',523343);

--实例化员工对象类型

o_emp:=employee_addr(7369,5000,800,20,o_address);

--输出员工信息

dbms_output.put_line('员工信息为'||o_emp.get_emp_info);

end;




对象继承

继承是指在已存在的对象类型的基础上建立新对象类型的一种技术,新定义的类可包含现有类所声明的数据,定义及包含新定义的对象类型所增加的声明的组合。

对象类型继承由父类型和子类型组成,其中父类型用于定义可供子类型使用的公共的属性和方法,而子类型不但可以使用这些公共的属性和方法,还可以具有自己私有的属性和方法。


例如,在对现实世界的实体进行抽象时,可以发现企业中的员工都有一些共性,因此将其抽象出一个父类型,用来定义一个基类,比如员工都包含了姓名,性别,出生日期等,可以基于这些特性定义一个person_obj的对象类型,然后在该类型的基础上继承一个子类employee_personobj类型,除了具有person_obj的特性外,还具有员工证件号码,工资,提成和职位信息,类继承结构。



实现person_obj父对象

create or replace type person_obj as object(

person_name varchar(20),

gender varchar2(2),

birthdate DATE,

address varchar2(50),

MEMBER FUNCTION get_info return varchar2

)NOT FINAL;


create or replace type body person_obj

as

MEMBER FUNCTION get_info return varchar2

is

begin

return '姓名:' || person_name||',家庭住址:'||address;

end;

end;



子对象employee_personpbj的实现

create or replace type employee_personobj under person_obj(

empno number(6),

sal number(10,2),

job varchar2(10),

MEMBER FUNCTION get_emp_info return varchar2

);


create or replace type body employee_personobj as

MEMBER FUNCTION get_emp_info return varchar2 is

begin

return '员工编号:' || SELF.empno ||'员工名称:' || SELF.person_name||'职位:'||SELF.job;

end;

end;


为了从一个父对象中继承,在子对象中使用UNDER关键字,指定一个父对象名称,该父对象必须使用NOT FINAL关键字定义的对象。在子对象中新增了属性和方法后,就被合并到父对象中去了,因此可以看到在对象体实现时,可以直接使用SELF关键字访问父对象中的person_name属性。


使用方法:

declare

o_emp employee_personobj;

begin

--使用构造函数实例化员工对象

o_emp:=employee_personobj('张小五','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'中信',7981,5000,'Programmer');

dbms_output.put_line(o_emp.get_info);

dbms_output.put_line(o_emp.get_emp_info);

end;



方法重载

所谓重载就是定义一个或多个具有同名的函数或过程,但是参数类型名个数不同,由编译器根据调用参数确定执行哪一个子程序。这种重载方式有时候也称为静态多态。在使用对象继承时,也可以使用方法重载。但是这种方法重载不同于过程或包中的重载,这种重载使用了动态方法调用的能力,也称为动态多态或运行时多态。也就是说具体的调用方法不是在编译时确定的,而是在代码实际执行时才确定的重载。

对象方法的重载使用OVERRIDING关键字,不是根据参数的个数来决定调用哪一个方法,而是根据优先级进行调用,也就是总是先调用子类的方法。


实现对象方法重载

create or replace type employee_personobj under person_obj(

empno number(6),

sal number(10,2),

job varchar2(10),

MEMBER FUNCTION get_emp_info return varchar2,

--定义重载方法

OVERRIDING MEMBER FUNCTION get_info return varchar2

);


create or replace type body employee_personobj as

MEMBER FUNCTION get_emp_info return varchar2 is

begin

return '员工编号:' || SELF.empno||'员工名称:'||SELF.person_name||'职位:'||SELF.job;

end;

--实现重载方法

OVERRIDING MEMBER FUNCTION get_info return varchar2 as

begin

return '员工编号:' ||SELF.empno||'员工名称:'||SELF.person_name||'职位':||SELF.job;

end;

end;



调用employee_personobj对象实例的get_info方法时,将看到的是来自重载方法中的消息

declare

o_emp employee_personobj;

begin

--使用构造函数实例化员工对象

o_emp:=employee_personobj('张小五','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'中信',7981,5000,'Programmer');

dbms_output.put_line(o_emp.get_info);

end;



管理对象表


对象表就像普通的表一样,只是存储的事对象类型,该表中的每一个字段与对象的一个属性相对应,然后使用对象表的每一行或者称为每一条记录存储一个对象类型的实例。

create table emp_obj_table of employee_personobj;


创建对象表使用的是create table  of 语句。

可以使用desc 查看到结构,和对象中包含的属性类型。

创建的对象表一旦引用了特定的对象类型,就不能使用drop type语句对对象类型进行删除,如果非要这样做,oracle会报错。


如果对象类型中包含嵌套的对象类型,那么嵌套的对象表类型会被作为一列存储到对象表中,比如employee_addr的addr属性是一个嵌套了address_type对象类型的属性,因此当创建一个employee_addr对象类型的数据表,会将addr作为一个单独的列。

create table emp_addr_table of employee_addr;

使用set desc depth all linenum on 语句。




插入对象表

插入数据的语法与向普通表插入数据一样,使用insert命令。

除了直接插入列值外,在向对象类型的表中插入数据时,可以先实例化一个对象类型,然后向insert语句的values子句传入构建的对象类型。

declare

o_emp employee_personobj;

begin

--使用构造函数实例化员工对象

o_emp:=employee_personobj('张小五','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'中信',7981,5000,'Programmer');

insert into emp_obj_table values(o_emp);

end;




检索对象表

对象表的查询与关系表的查询一样。


1.value函数

在查询语句中使用value函数将返回存储在对象表中的对象实例,因此对于查询的单行记录,可以使用select into 语句将查询出来的值插入到预定义的对象实例中;对于查询返回的多行记录,可以使用游标来获取查询出来的对象实例。


select value(e) from emp_obj_table e;

该查询返回了存储在emp_obj_table表中所有的对象实例,每一行一个对象实例。

下面演示使用select into 将查询的结果赋值给一个对象类型的变量:

declare

o_emp employee_personobj;

begin

--使用select into 语句将value函数返回的对象实例插入到对象类型的变量

select value(e) into o_emp from emp_obj_table e where e.person_name='张小五';

--输出对象类型的属性值

dbms_output.put_line(o_emp.person_name||'的职位是:'||o_emp.job);

end;


使用游标和value函数查询多行数据结果

declare

o_emp employee_personobj;

cursor all_emp

is

select value(e) as emp from emp_obj_table e;

begin

for each_emp in all_emp

loop

o_emp:=each_emp.emp;

--输出对象实例信息

dbms_output.put_line(o_emp.person_name||'的职位是:'||o_emp.job);

end loop;

end;




2.REF函数


与value相对应的ref函数也可以用来检索对象表中的数据,但是由其名可知,它返回的事一个对象的引用。二者之间的主要区别在于引用类型返回的只是指向对象实际位置的一个指针,而value类型会把对象副本从一个子程序传递到另一个子程序,程序执行时的效率可能会降低。


使用共享对象类型,可以避免数据不必要的重复,而且在共享的内容更新时,任何引用所指向的内容也会被立即更新。

create type address as object(      --创建地址类型

street varchar2(35),

city varchar2(15),

state char(2),

zip_code integer

);


create table addresses of address;  --创建地址对象表

create type person as object(       --创建人员对象类型

person_name varchar2(15),

birthday DATE,

home_address REF address, --使用ref关键字,指定属性为指向另一个对象表的对象

phone_number varchar2(15)

);


create table persons of person;   --创建人员对象表

插入数据

insert into addresses values(address ('玉兰','深圳','GD','321413'));


insert into persons values(person ('王小五',TO_DATE('1983-01-01','YYYY-MM-DD'),(select ref(a) from addresses a where street='玉兰'),'1312332'));



使用PL/SQL语句块向对象表中插入引用类型的对象

declare

addref ref address;  --定义一个引用类型的对象

begin

select ref (a) into addref from addresses a where street='玉兰';

insert into persons values(person('武大郎',TO_DATE('1983-01-01','YYYY-MM-DD'),addref,'1312312'));

end;


在语句块中使用ref关键字定义了一个指向address对象类型的引用类型,然后使用ref函数从address表中返回匹配的引用类型的指针,最后将这个引用类型作为home_address属性的值插入到persons表中.

当对象表中包含引用类型时,如果直接使用select语句进行查询,引用类型的列是一串数字码,而且引用类型的对象无法直接访问其属性。

select person_name,home_address from persons;


如果使用了deref函数,则可以查询到引用类型所指向的地址类型的值。

select person_name,deref (home_address) as home from persons;

在PL/SQL语句中使用ref类型的变量时,不能直接访问对象的属性,必须首先通过deref函数解除引用后,才能使用对象类型的属性.



更新对象表

更新对象表时既可以把对象表看作是一个普通的关系型表,与关系表一样把对象表中的每个属性作为一列调用作为一列调用update语句,也可以将一行看作是一个对象,在update语句中对对象进行赋值。

update emp_obj_table empobj set empobj.gender='M' where empobj.person_name='张小五';


另一种方法是直接更新一个对象表中的对象实例,因此需要先实例化一个对象实例。

update emp_obj_table empobj set empobj=employee_personobj('李晓琪','F',TO_DATE('1983-01-01','YYYY-MM-DD'),'众泰',7981,7000,'Testing') where person_name='张小五';



在操纵对象表时,还可以在where子句中把一行看作一个对象,可以使用对象标识符来唯一标识要更改的对象,对象标识符是对象表中为了唯一标识一条记录而定义的一串数字值,对于一个对象来说,需要使用REF函数来获取对象的标识符,在where语句中使用对象类型进行检索的示例


在where子句中使用对象类型

declare

emp_ref ref employee_personobj; --定义引用对象类型

begin --从对象表中获取对刘小燕的对象引用

select ref(e1) into emp_ref from emp_obj table e1 where person_name='刘小燕';

update emp_obj_table emp_obj set emp_obj=employee_personobj('何晓峰','F',TO_DATE('1985-08- 01','YYYY-MM-DD'),'本田',7981,7000,'developer') where ref (emp_obj)=emp_ref;

end;


如果对象表的属性列表中包含了引用类型,也就是说包含了指向行对象数据的指针,如果要修改其列所引用的数据,就必须修改相应的行对象。例如在persons表中,home_address字段是一个指向address对象类型的引用,因此如果要update这个包含引用类型的表。

declare

addr address;

begin

select deref(home_address) into addr from persons where person_name='张小五';

addr.street:='五一';

update address set street=addr.street where zip_code='21312';

end;

先使用deref函数返回指针所指向的address对象实例的引用,然后将更改的信息更新回address表中就完成了对引用记录的更改。




删除对象表

与删除普通表类似,对象表的删除使用delete语句,下面的语句像删除普通的关系表一样删除emp_obj_table表中员工名称为张小五的记录

delete from emp_obj_table where person_name='张小五';


与update类似,还可以在where子句中使用引用类型的对象进行删除。例如要删除emp_obj_table表中员工名称为刘小燕的记录。

declare

emp_ref ref employee_personobj;

begin

select ref (e1) into emp_ref from emp_obj_table e1 where person_name='刘小燕'; --从对象表中获取对刘小燕的对象引用

delete from emp_obj_table emp_obj where ref (emp_obj)=emp_ref

end;        --使用delete语句删除emp_obj_table表中刘小燕的记录


通过使用ref函数,将员工名称为刘小燕的记录进行了正确的删除。




创建对象列

除了将整个对象作为表中的列来存储的对象表之外,还可以为关系表中的某一列的属性指定为对象类型,这种表称为带对象列的关系表。这种带对象类型的关系表与对象表的主要不同在于对象表时通过使用对象标识符来引用对象实例的,而对于列对象来说是没有oracle对象标识符的,列对象的对象实例属于关系型数据库中的记录,具有一个rowid值作为标识符。


定义dept_obj对象类型

create or replace type dept_obj as object(

deptno number(10),

dname varchar2(30),

loc varchar2(30),

MEMBER FUNCTION get_dept_info return varchar2

) INSTANTIABLE NOT FINAL;


定义对象类型体

create or replace type body dept_obj as

MEMBER FUNCTION get_dept_info return varchar2 is

begin

return '部门编号:' || SELF.deptno ||'部门名称:'||SELF.dname||'职位:'||SELF.loc;

end;

end;



对象类型dept_obj封装了部门信息,可以看到具有部门编号,部门名称和部门的地址信息,接下来创建一个名为emp_colobj的表,该表用来存放员工信息,但是在部门列中,将使用dept_obj对象类型作为列类型,可以再表中存储关于部门的详细信息。


create table emp_colobj(

empno number(10) NOT NULL primary key,

ename varchar2(30),

job varchar2(30),

sal number(10,2),

deptcol dept_obj

) --dept列指定为dept_obj对象类型


在对象表中绑定了某个对象类型后,不能直接对对象表中的列及类型进行操作,比如新增列或对列进行修改等,但是关系表中包含对象列类型则没有这个限制,可以使用标准的ALTER TABLE语句来进行操作.





使用对象视图

为了创建一个对象视图,必须首先创建一个与底层的数据表的列具有相匹配属性的对象类型。


定义emp_tbl_obj对象类型

create or replace type emp_tbl_obj as object(

empno number(6),

ename varchar2(10),

job varchar2(18),

mgr number(4),

hiredate DATE,

sal number(7,2),

comm number(7,2),

deptno number(2),

MEMBER FUNCTION get_emp_info return varchar2

)INSTANTIABLE NOT FINAL;


create or replace type body emp_tbl_obj as

MEMBER FUNCTION get_emp_info return varchar2 is

begin

return '员工编号:' ||SELF.empno||'员工名称:'||SELF.ename||'职位:'||SELF.job;

end;

end;


在创建对象视图时,必须要确定要使用的OID(对象标识符).对象标识符是用来唯一标识一行对象的一个字符串,通过OID可以保证对对象实例引用的唯一性。OID标识符仅在对象表和对象视图上被创建,一旦一个OID被赋给了一个对象,那么将永远属于那个对象。通常情况下,oracle会自动产生OID值,不需要要手工干预,但是在定义对象视图时,可以覆盖系统产生OID的机制,更改为使用对象表的主键来替代.


create view view_name of object_name

with object identifier(primary_key)

as

sql_statement;


view_name指定对象视图的名称,object_name用于指定对象视图的对象名称,with object identifier用于指定OID方式,可以指定表的主键作为OID,sql_statement是对关系表的sql查询语句时。


create view emp_view

of emp_tb1_obj

with object identifier(empno)

as

select e.empno,e.ename,e.job,e.mgr,e.hiredate,e.sal,e.comm,e.deptno from emp e;


使用对象类型的视图

declare

o_emp emp_tb1_obj;

begin

--查询对象类型

select value(e) into o_emp from emp_view e where empno=7369;

--输出对象类型的属性

dbms_output.put_line('员工'||o_emp.ename||'的薪资为:'||o_emp.sal);

dbms_output.put_line(o_emp.get_emp_info);  --调用对象类型的成员方法

end;




管理对象类型


1.查看对象类型

COL type_name format A20;

select type_name,attributes,final,typecode from user_types where type_name like 'EMP%' and typecode='OBJECT';  


通过使用select connect by语句,还可以根据type_name和supertype_name来查询对象的继承结构.

set pagesize 500;

select rpad (' ',3 * (LEVEL-1)) |from user_types where typecode='OBJECT' CONNECT BY PRIOR type_name=superty




2.修改对象类型

如果已经基于对象类型建立了其他的对象类型或对象表,那么在为对象类型增加或删除属性时,必须要使用cascade关键字.

--添加mgr属性

alter type employee_personobj add attribute mgr number(6) cascade;

--删除sal属性

alter type employee_personobj drop attribute sal cascade;



修改对象类型的成员方法

alter type employee_personobj drop member function get_emp_info return varchar2 cascade;

--新增一个get_employee的成员方法

alter type employee_personobj add member function get_employee return varchar2 cascade;


--更改对象类型体,以便增加在对象类型规范中定义的方法

create or replace type body employee_personobj as

member function get_employee return varchar2 is

begin

return '员工编号:' ||SELF.empno||'员工名称:'||SELF.person_name||'职位:'||SELF.job;

end;

end;



如果从基类删除一个方法,那么也必须修改覆盖被删除方法的子类,可以用alter type 的casade选择来判断是否有子类被影响到:如果有子类覆盖了方法,那么语句被回滚。为了能成功地从基类删除一个方法。

1.先从子类删除方法

2.从基类删除方法,然后用不带overriding关键字的alter type把它重新添加进去.


      本文转自潘阔 51CTO博客,原文链接:http://blog.51cto.com/pankuo/1630258,如需转载请自行联系原作者






上一篇:Kafka消费过程关键源码解析


下一篇:使用Java实现B/S系统常见的获取客户端信息功能!