下面详细介绍下Oracle面向对象编程。
CREATE OR REPLACE TYPE type_name AS OBJECT(
--属性的声明
propertyname1 TYPE1,
propertyname2 TYPE2,
...
properynamen TYPEn,
--函数的声明
member function funcname1(param1 TYPE1, ...) return TYPE11,
static function funcname2(param1 TYPE2, ...) return TYPE22,
...
--过程的声明
member procedure procname1(param1 TYPE1, ...),
static procedure procname2(param1 TYPE2, ...),
...
);
对象类型体定义语法:
CREATE OR REPLACE TYPE BODY type_name --No 'AS OBJECT'
AS --NO 'BEGIN'
member function funcname1 return TYPE11
IS
//变量定义
BEGIN
//处理过程
return var1;
END funcname1;
static function funcname2 return TYPE22
IS
//变量定义
BEGIN
//处理过程
return var2;
END funcname2;
...
member procedure procname1(param1 TYPE1,...)
IS
//变量定义
BEGIN
//处理过程
END procname1;
static procedure procname2(param1 TYPE2,...)
IS
//变量定义
BEGIN
//处理过程
END procname2;
...
END;
说明: 1. 方法有成员方法和静态方法,过程也有成员过程和静态过程。成员方法和成员过程通过关键字member标识,静态方法 和静态过程通过关键字static标识。 2. 静态方法或静态过程能直接被对象类型调用,但不能被对象实例调用(区别java)。成员方法或成员过程只能被对 象类型的实例调用,不能直接被对象类型调用。 3. 在静态方法或过程中不能访问对象类型的属性。
下面定义一个NAME_TYPE对象类型,语法如下:
--对象类型头声明
CREATE OR REPLACE TYPE NAME_TYPE AS OBJECT(
firstname varchar2(100),
lastname varchar2(100),
static function buildname(fname varchar2, lname varchar2) return varchar2,
member function getname return varchar2,
member procedure changefirstname(cfname varchar2),
static procedure writename(clname varchar2)
);
--对象类型体定义
CREATE OR REPLACE TYPE BODY NAME_TYPE
AS
static function buildname(fname varchar2, lname varchar2) return varchar2
IS
name varchar2(200);
BEGIN
name := fname||' '||lname;
return name;
END buildname;
member function getname return varchar2
IS
name varchar2(200);
BEGIN
name := firstname||' '||lastname;
return name;
END getname;
member procedure changefirstname(cfname varchar2)
IS
BEGIN
firstname := cfname;
END changefirstname;
static procedure writename(clname varchar2)
IS
BEGIN
dbms_output.put_line(clname);
END writename;
END;
疑问:为什么在过程中可以向对象属性直接赋值,但是在函数中却不能?
测试代码:
declare
v_name VARCHAR2(100);
v_name_t NAME_TYPE;
begin
--对象类型直接调用静态方法writename
dbms_output.put_line('-------OBJECT TYPE CALL STATIC PROCEDURE writename------');
NAME_TYPE.writename('LIGUOLIANG');
--对象类型直接调用静态方法buildname
dbms_output.put_line('-------OBJECT TYPE CALL STATIC FUNCTION buildname------');
v_name := NAME_TYPE.buildname('ZHUGE', 'LIANG');--创建对象类型实例
dbms_output.put_line(v_name);
--对象类型实例调用成员方法getname
dbms_output.put_line('-------OBJECT TYPE INSTANCE CALL MEMBER FUNCTION buildname------');
v_name_t := NAME_TYPE('LIU', 'BEI');
dbms_output.put_line(v_name_t.getname);
end;
执行结果如下:
-------OBJECT TYPE CALL STATIC PROCEDURE writename------
LIGUOLIANG
-------OBJECT TYPE CALL STATIC FUNCTION buildname------
ZHUGE LIANG
-------OBJECT TYPE INSTANCE CALL MEMBER FUNCTION buildname------
LIU BEI
create table T_PERSON
(
gid NUMBER(38) not null,
name NAME_TYPE,
age NUMBER(5),
sex VARCHAR2(1),
description VARCHAR2(500),
constraint PK_PERSON_GID primary key (gid)
);
语句的增删改查:(访问使用对象类型的表时,如果访问了对象类型的属性,访问的属性前一定要用表的别名)
declare
v_name NAME_TYPE;
v_sex VARCHAR2(1);
i_age number(3);
begin
--查询语句
SELECT name, sex, age INTO v_name, v_sex, i_age FROM t_person WHERE gid = 1;
dbms_output.put_line('NAME: '||v_name.firstname||' '||v_name.lastname);
--新增语句
INSERT INTO t_person VALUES(3, NAME_TYPE('HE', 'MA'), 23, '1', '');
--修改语句,因为要访问对象类型的属性,对象属性前必须要用表的别名
UPDATE t_person t SET t.name.firstname = 'QQ' WHERE t.gid = 1;
UPDATE t_person SET name = NAME_TYPE('XX', 'SANFENG') WHERE gid = 2;
--删除语句,因为要访问对象类型的属性,对象属性前必须要用表的别名
DELETE FROM t_person t WHERE t.name.firstname = 'HE';
COMMIT;
end;
在建立对象类型时,Oracle会自动为对象类型生成相应的构造方法,方法名和对象类型名相同,该构造方法的参数列表是该对象类型的所有属性,并且构造方法用于初始化对象实例。实例化语法如下:
type_name(param1, ...)
举例:
1. 向t_person表插入数据,脚本如下:
INSERT INTO t_person VALUES(2, NAME_TYPE('ZHAN', 'SAN'), 28, '1', 'GOOD PERSON');
COMMIT;
注:向使用自定义对象类型的表插入数据,只能用构造方法。
2. 在过程中使用,脚本如下:
declare
v_name NAME_TYPE := NAME_TYPE('LI', 'LEI');
begin
dbms_output.put_line(v_name.firstname);
dbms_output.put_line(v_name.lastname);
end;
输出结果如下:
LI
LEI
在过程或方法代码分支条件,或是在SQL语句的条件中,常会用到变量或字段的比较的表达式。基本变量比较我们都比较熟悉了,而对象类型中通常包含几个属性,那么以它为类型的变量或字段大小的比较是怎么进行的呢?解决办法由两个,通过定义映射方法或是排序方法,他们都是制定比较规则的。
映射方法和排序方法不能同时存在,且一个对象类型中最多只能有一个映射方法或排序方法。在创建映射方法后,如果用到where 或 order by 以及 <>= 等比较关系时,自动调用映射方法。
如果对象类型中没有映射方法或排序方法,不能比较对象类型实例的大小,否则会报错。
下面分别详细介绍:
映射方法通过关键字map声明,不带任何参数,有一个返回值。一个对象类型最多只有一个映射方法,如果一个对象类型没有映射方法,那么这个对象类型的实例或字段就不能比较大小。比如:
create or replace type person as object
(
NAME varchar2(50),
SEX varchar2(10),
BIRTHDATE date ,
PLACE varchar2(100),
map member function compare2 return date
);
create or replace type body person
is
map member function compare2 return date
is
begin
return self.birthdate;
end compare2;
end;
测试代码:
declare
person_one person;
person_two person;
begin
person_one:=person( 'LISI' , 'NAN' , date '2008-10-20' , 'SHANGHAI' );
person_two:=person( 'ZHANGSHAN' , 'NV' , date '2008-10-11' , 'HANZHOU' );
if person_one > person_two then
dbms_output.put_line(person_one.name|| ' bigger than ' ||person_two.name );
elsif person_one < person_two then
dbms_output.put_line(person_two.name|| ' bigger than ' ||person_one.name );
else dbms_output.put_line( ' same ' );
end if ;
end ;
运行结果:
LISI bigger than ZHANGSHAN
create or replace type person as object
(
NAME varchar2(50),
SEX varchar2(10),
BIRTHDATE date ,
PLACE varchar2(100),
order member function match(p_person person) return integer
);
create or replace type body person
is
order member function match(p_person person) return integer
is
begin
if self.birthdate > p_person.birthdate then
return 1 ;
elsif self.birthdate < p_person.birthdate then
return - 1 ;
else return 0 ;
end if ;
end match;
end;
测试代码:
declare
person_one person;
person_two person;
begin
person_one:=person( 'LISI' , 'NAN' , date '2008-10-20' , 'SHANGHAI' );
person_two:=person( 'ZHANGSHAN' , 'NV' , date '2008-10-11' , 'HANZHOU' );
if person_one > person_two then
dbms_output.put_line(person_one.name|| ' bigger than ' ||person_two.name );
elsif person_one < person_two then
dbms_output.put_line(person_two.name|| ' bigger than ' ||person_one.name );
else dbms_output.put_line( ' same ' );
end if ;
end ;
运行结果:
LISI bigger than ZHANGSHAN
对象类型实例.propertyname
对象类型实例.functionname(paramvalue1,... )
对象类型实例.procedurename(paramvalue1, ...)
举例:
declare
v_name NAME_TYPE;
v_sex varchar2(1);
v_age number(3);
begin
select name, sex, age INTO v_name, v_sex,v_age from t_person where gid = 1;
dbms_output.put_line('-----------修改firstname前------------');
dbms_output.put_line(v_name.firstname);
dbms_output.put_line(v_name.getname);--无参函数有无括号都可以
v_name.changefirstname('WANG');
dbms_output.put_line('-----------修改firstname后------------');
dbms_output.put_line(v_name.firstname);
dbms_output.put_line(v_name.getname());--无参函数有无括号都可以
end;
输出结果:
-----------修改firstname前------------
LI
LI GUOLIANG
-----------修改firstname后------------
WANG
WANG GUOLIANG
可变数组,是一种集合。一个可变数组是对象的一个集合,其中每个对象都具有相同的数据类型。可变数组的大小由创建时决定。在表中建立可变数组后,可变数组在主表中作为一个列对待。
语法如下:
CREATE OR REPLACE TYPE varray_name AS VARRAY(n) OF type;
语法说明:
1. varray_name :数组名。
2. n:数组的包含元素的最大个数。如果插入数组的元素大于这个上限,将会报错。
3. type:数组的类型,可以是基本变量,如varchar2,integer等,也可以是自定义的对象类型,如上面定义的NAME_TYPE。
4. first属性、last属性和count属性分别获得数组的首元素、末元素和总数。如有一个表
记录变量v_student_tbl,那么v_student_tbl.first获得是表记录的首元素。在进行FOR或FORALL循环
时比较常用。
举例:
一个“公司”表t_company,有一个字段包含所有员工的姓名,脚本如下:
--创建数组
CREATE OR REPLACE TYPE name_list AS VARRAY(3) OF NAME_TYPE;
--使用数组定义表
CREATE TABLE t_company
(
gid NUMBER(38) not null,
name VARCHAR2(100),
address VARCHAR2(100),
employeeList NAME_LIST,
constraint PK_COMPANY_GID primary key (gid)
);
--向表中插入数据
INSERT INTO t_company VALUES(1, 'dakongying', 'beijing', NAME_LIST(NAME_TYPE('ZHAO', 'RUJUN'), NAME_TYPE('CHEN', 'DA')));
COMMIT;
--查询数据
SELECT * FROM TABLE(SELECT employeeList from t_company where gid = 1);
如果向数组赋值之前,必须初始化,初始化时,传入元素的大小必须大于等于赋值的个数,但是又要小于等于数组定义的元素上限。初始化时可以通过传入null,先分配一个空间,如下段代码所示:
declare
v_namelist NAME_LIST := NAME_LIST(NULL, NULL,NULL);
begin
select name into v_namelist(1) from t_person where gid = 1;
select name into v_namelist(2) from t_person where gid = 2;
dbms_output.put_line(v_namelist(1).firstname);
dbms_output.put_line(v_namelist(1).lastname);
dbms_output.put_line(v_namelist(2).firstname);
dbms_output.put_line(v_namelist(2).lastname);
dbms_output.put_line('List length:' || v_namelist.count);--数组的count属性,返回数组元素的个数,是初始化的个数,包含NULL元素
end;
输出结果:
LI
GUOLIANG
ZHAN
SAN
List length:2
CREATE OR REPLACE TYPE table_name AS TABLE OF type;
语法说明:
1. table_name :嵌套表名。
2. type:数组的类型,可以是基本变量,如varchar2,integer等,也可以是自定义的对象类型,如上面定义的NAME_TYPE。
3. first属性、last属性和count属性分别获得表记录(或称为数组)的首元素、末元素和总数。如有一个表
记录变量v_student_tbl,那么v_student_tbl.first获得是表记录的首元素。在进行FOR或FORALL循环
时比较常用。
继承父类的子类对象类型将有父类的所有属性、方法和过程。 父类型必须声明为NOT FINAL,子类型使用关键字UNDER。
举例:
--创建父类型
CREATE TYPE animal_type AS OBJECT(
name VARCHAR2(50),
hair VARCHAR2(50),
foot VARCHAR2(50)
) NOT FINAL;
--子类型继承父类型
CREATE TYPE cat_type UNDER animal_type(
paw VARCHAR2(50)
);
如果父类没有声明为NOT FINAL,子类在继承的时候将报错:Error: PLS-00590: attempting to create a subtype UNDER a FINAL type。
重写就是在子类中对父类又有的方法或过程重新实现。重写关键字为overriding,在子类中把要重写的方法或过程声明和实现之前加上该关键字。
举例:
定义头部:
CREATE OR REPLACE TYPE cat_type UNDER annimal_type
(
paw VARCHAR2(50),
OVERRIDING MEMBER PROCEDURE PROC_RUN
)
定义主体:
CREATE OR REPLACE TYPE BODY cat_type
IS
OVERRIDING MEMBER PROCEDURE PROC_RUN
IS
BEGIN
//重新实现
END;
END;
创建对象表的语法:
CREATE TABLE table_name OF object_type;
语法说明:
1.table_name:对象表名称,执行创建对象表语句后,数据库中将会生成一个名字为table_name的表。
2.object_type:对象类型,生成的表的字段和对象类型时对应的。
举例:
CREATE TABLE t_name OF NAME_TYPE;
执行上面语句后,数据库将生成一个t_name表,这个表就是对象表。
对象表之间没有主外键关联的概念,为了体现这层关系,oracle中用了ref对象来实现。
下面介绍下相关操作法和函数:
1. ref操作符:声明引用类型。如 name ref NAME_TYPE, 变量或字段name就是引用类型,存储NAME_TYPE型对象的OID。
2. ref(表的别名)函数:获得对象表中对象OID值,如select ref(a) from otable a。
3. deref(OID)函数:通过OID找到并返回行对象表中对象。
下面通过一个例子说明对象表直接的关联:
--员工对象类型
CREATE OR REPLACE TYPE employee AS OBJECT(
cardId VARCHAR2(100),
address VARCHAR2(100),
sex VARCHAR2(1),
name REF NAME_TYPE, --通过REF操作符,表示该字段引用NAME_TYPE对象,该字段实际存储的时对象的OID
age INTEGER
);
--创建t_employee对象表
CREATE TABLE t_employee OF employee;
--向NAME_TYPE的对象表t_name插入数据
INSERT INTO t_name VALUES('LI', 'KUI');
INSERT INTO t_name VALUES('ZHANG', 'LAN');
INSERT INTO t_name VALUES('CHEN', 'MING');
COMMIT;
--向员工表插入数据
INSERT INTO t_employee VALUES(
'101',
'beijing',
'1',
(SELECT REF(n) FROM t_name n where n.firstname = 'LI'), --通过ref(表别名)获得对象的引用
23
);
INSERT INTO t_employee VALUES(
'102',
'shanghai',
'0',
(SELECT REF(n) FROM t_name n where n.firstname = 'ZHANG'),--通过ref(表别名)获得对象的引用
23
);
COMMIT;
--通过声明一个引用变量,新增数据
DECLARE
-- 声明NAME_TYPE类型的引用
name_ref REF NAME_TYPE;
BEGIN
SELECT REF(n) INTO name_ref FROM t_name n where n.firstname = 'CHEN';
INSERT INTO t_employee VALUES(
'103',
'chengdu',
'1',
name_ref,
24
);
COMMIT;
END;
--查询员工的信息,使用deref来获得对象字段的值
SELECT t.cardId, t.address, deref(t.name) from t_employee t;
--更新引用类型,即赋予新的对象的OID值
UPDATE t_employee t SET name = (SELECT ref(n) FROM t_name n WHERE n.firstname = 'ZHANG') WHERE t.cardId = '103';
DELETE FROM t_employee WHERE name = (SELECT ref(n) FROM t_name n WHERE n.firstname = 'ZHANG');