类类型
DM7通过类类型在DMSQL程序中实现面向对象编程的支持。类将结构化的数据及对其进行操作的过程或函数封装在一起。允许用户根据现实世界的对象建模,而不必再将其抽象成关系数据。
DM7的类类型分为普通类类型和JAVA CLASS类型。DM文档中的示例除了特别声明使用的是JAVA CLASS类型,要不然使用的都是普通类类型。
普通CLASS类型
DM7的类的定义分为类头和类体两部分,类头完成类的声明;类体完成类的实现。类中可以包括以下内容:
1. 类型定义
在类中可以定义游标、异常、记录类型、数组类型、以及内存索引表等数据类型,在类的声明及实现中可以使用这些数据类型;类的声明中不能声明游标和异常,但是实现中可以定义和使用。
2. 属性
类中的成员变量,数据类型可以是标准的数据类型,可以是在类中自定义的特殊数据类型。
3. 成员方法
类中的函数或过程,在类头中进行声明;其实现在类体中完成;
成员方法及后文的构造函数包含一个隐含参数,即自身对象,在方法实现中可以通过this或self来访问自身对象,self等价于this。如果不存在重名问题,也可以直接使用对象的属性和方法。this和self只能在包或对象脚本中调用。
4. 构造函数
构造函数是类内定义及实现的一种特殊的函数,这类函数用于实例化类的对象,构造函数满足以下条件:
1) 函数名和类名相同;
2) 函数返回值类型为自身类。
构造函数存在以下的约束:
1) 系统为每个类提供两个默认的构造函数,分别为0参的构造函数和全参的构造函数;
2) 0参构造函数的参数个数为0,实例的对象内所有的属性初始化值为NULL;
3) 全参构造函数的参数个数及类型和类内属性的个数及属性相同,按照属性的顺序依次读取参数的值并给属性赋值;
4) 用户可以自定义构造函数,一个类可以有多个构造函数,但每个构造函数的参数个数必须不同;
5) 如果用户自定义了0个参数、或参数个数同属性个数相同的构造函数,则会覆盖相应的默认构造函数。
下面从类的声明、类的实现、类的删除、类体的删除和类的使用几部分来详细介绍类类型的实现过程。
类的声明在类头中完成。类头定义通过CREATE CLASS语句来完成,其语法为:
语法格式
CREATE [OR REPLACE] CLASS [<模式名>.]<类名> [WITH ENCRYPTION] [UNDER [<模式名>.]<父类名>] [[NOT] FINAL] [[NOT]
INSTANTIABLE] [AUTHID DEFINER | AUTHID CURRENT_USER] AS|IS<类内声明列表> END [类名]
<类内声明列表> ::=<类内声明>;{<类内声明>;}
<类内声明> ::=<变量定义>|<过程定义>|<函数定义>|<类型声名>
<变量定义> ::=<变量名列表><数据类型> [默认值定义]
<过程定义> ::= [<方法继承属性>][STATIC|MEMBER] PROCEDURE<过程名><参数列表>
<函数定义> ::= [<方法继承属性>] [MAP] [STATIC|MEMBER] FUNCTION<函数名><参数列表> RETURN<返回值数据类型>[DETERMINISTIC]
[PIPELINED]
<重载属性> ::= [NOT] OVERRDING
::= FINAL | NOT FINAL | INSTANTIABLE | NOT INSTANTIABLE
<类型声名> ::= TYPE<类型名称> IS<数据类型>
使用说明
1.类中元素可以以任意顺序出现,其中的对象必须在引用之前被声明;
2.过程和函数的声明都是前向声明,类声明中不包括任何实现代码;
3.支持对象静态方法声明与调用。可以在PROCEDURE/FUNCTION关键字前添加static保留字,以此指明方法为静态方法。静态方法只能以对象名为前缀调用,而不能在对象实例中调用;
4.支持对象成员方法声明与调用。可以在PROCEDURE/FUNCTION关键字前添加MEMBER以指明方法为成员方法。MEMBER与STATIC不能同时使用,非STATIC类型的非构造函数方法默认为成员方法。MAP表示将对象类型的实例映射为标量数值,只能用于成员类型的FUNCTION;
5.关于类继承,有以下使用限制:
1) 类定义默认为FINAL,表示该对象类型不能被继承,定义父类时必须指定NOT FINAL选项;
2) 定义子类时必须指定UNDER选项;
3) NOT INSTANTIABLE对象不能为FINAL;
4) NOT INSTANTIABLE对象不能实例化,但是可以用其子类赋值;
5) 对象实例化时,必须对父类和子类的成员变量都赋值,且从父类到子类逐个赋值;
6) 不支持对象的循环继承;
7) 不支持对象的多继承,即一个类有多个父类;
6) 不支持对象的循环继承;
7) 不支持对象的多继承,即一个类有多个父类;
8) 不支持父类和子类包含同名变量;
9) 父类和子类可以同名同参,此时子类必须指定OVERRIDING;
10) 方法默认为NOT OVERRIDING,OVERRIDING不能与static一起使用;
11) 父类和子类支持同名不同参(参数个数不同、参数个数相同但类型不同)的方法;
12) 同名且参数个数相同但类型不同时,根据参数类型选择使用的方法;
13) 方法默认为INSTANTIABLE,如果声明为NOT INSTANTIABLE,则不能与FINAL、STATIC一起使用;
14) 如果父类有多个NOT INSTANTIABLE方法,子类可以只部分重写,但此时子类必须定义为NOT FINAL NOT INSTANTIABLE;
15) NOT INSTANTIABLE方法不能具有主体;
16) 方法默认为NOT FINAL,如果声明为FINAL,则不能被子类重写;
17) 子类可以赋值给父类;
18) 如果父类对应的实例是子类或者子类的孩子,则该父类可以赋值给子类;
19) 可以用INSTANTIABLE子类对NOT INSTANTIABLE父类进行赋值;
20) 子类实例赋值给父类后,调用时使用的是父类方法而不是子类方法;
21) 支持使用as语句转换为父类。
所需权限
1、使用该语句的用户必须是DBA或具有CREATE CLASS数据库权限的用户;
2、可以用关键字AUTHID DEFINER |AUTHID CURRENT_USER指定类的调用者权限,若为DEFINER,则采用类定义者权限,若为CURRENT_USER则为当前用户权限,默认为类定义者权限。
类的实现通过类体完成。类体的定义通过CREATE CLASS BODY语句来完成,其语法为:
语法格式
CREATE [OR REPLACE] CLASS BODY [<模式名>.]<类名> [WITH ENCRYPTION] AS|IS<类体部分> END [类名]
<类体部分> ::=<过程/函数列表> [<初始化代码>]
<过程/函数列表> ::=<过程实现|函数实现>{,<过程实现|函数实现> }
<过程实现> ::= [<方法继承属性>][STATIC|MEMBER]PROCEDURE<过程名><参数列表> AS|IS BEGIN<实现体> END [过程名]
<函数实现> ::= [<方法继承属性>][MAP] [STATIC|MEMBER]FUNCTION<函数名><参数列表> RETURN<返回值数据类型>[DETERMINISTIC] [PIPELINED] AS|IS BEGIN<实现体> END [函数名]
<重载属性> ::= [NOT] OVERRDING
::= FINAL | NOT FINAL | INSTANTIABLE | NOT INSTANTIABLE
<初始化代码> ::= [[<说明部分>]BEGIN<执行部分>[<异常处理部分>]]
<说明部分> ::=[DECLARE]<说明定义>{<说明定义>}
<说明定义>::=<变量说明>|<异常变量说明>|<游标定义>|<子过程定义>|<子函数定义>
<变量说明>::=<变量名>{,<变量名>}<变量类型>[DEFAULT|ASSIGN|:=<表达式>];
|<[模式名.]表名.列名%TYPE>|<[模式名.]表名%ROWTYPE>|<记录类型>
<记录类型>::= RECORD(<变量名>
})
<异常变量说明>::=<异常变量名>EXCEPTION[FOR<错误号>]
<异常处理语句>::= WHEN<异常名> THEN<SQL过程语句序列>
使用说明
1. 类声明中定义的对象对于类体而言都是可见的,不需要声明就可以直接引用。这些对象包括变量、游标、异常定义和类型定义;
2. 类体中的过程、函数定义必须和类声明中的声明完全相同。包括过程的名字、参数定义列表的参数名和数据类型定义;
3. 类中可以有重名的成员方法,要求其参数定义列表各不相同。系统会根据用户的调用情况进行重载(OVERLOAD);
4. 声明类与实现类时,对于确定性函数的指定逻辑与包内函数相同。目前不支持类的确定性函数在函数索引中使用。
所需权限
使用该语句的用户必须是DBA或该类对象的拥有者且具有CREATE CLASS数据库权限的用户。
完整的类头、类体的创建如下所示:
—-类头创建
SQL> create or replace class mycls 2 as 3 type rec_type is record (c1 int, c2 int); --类型声明 4 id int; --成员变量 5 r rec_type; --成员变量 6 function f1(a int, b int) return rec_type; --成员函数 7 function mycls(id int , r_c1 int, r_c2 int) return mycls; 8 --用户自定义构造函数 9 end; 10 / executed successfully used time: 14.032(ms). Execute id is 106.
—-类体创建
SQL> create or replace class body mycls 2 as 3 function f1(a int, b int) return rec_type 4 as 5 begin 6 r.c1 = a; 7 r.c2 = b; 8 return r; 9 end; 10 function mycls(id int, r_c1 int, r_c2 int) return mycls 11 as 12 begin 13 this.id = id; --可以使用this.来访问自身的成员 14 r.c1 = r_c1; --this也可以省略 15 r.c2 = r_c2; 16 return this; --使用return this 返回本对象 17 end; 18 end; 19 / executed successfully used time: 61.783(ms). Execute id is 107.
重编译类
重新对类进行编译,如果重新编译失败,则将类置为禁止状态。
重编功能主要用于检验类的正确性。
语法格式
ALTER CLASS [<模式名>.]<类名> COMPILE [DEBUG];
参数
1.<模式名> 指明被重编译的类所属的模式;
2.<类名> 指明被重编译的类的名字;
3.[DEBUG] 可忽略。
所需权限
执行该操作的用户必须是类的创建者,或者具有DBA权限。
举例说明
例如重新编译类
SQL> ALTER CLASS mycls COMPILE; executed successfully used time: 8.867(ms). Execute id is 108.
删除类
类的删除分为两种方式:一是类头的删除,删除类头则会顺带将类体一起删除;另外一种是类体的删除,这种方式只能删除类体,类头依然
存在。
删除类头
类的删除通过DROP CLASS完成,即类头的删除。删除类头的同时会一并删除类体。
语法格式
DROP CLASS [<模式名>.]<类名>[RESTRICT | CASCADE]; 使用说明
1.如果被删除的类不属于当前模式,必须在语句中指明模式名;
2.如果一个类的声明被删除,那么对应的类体被自动删除。
所需权限
执行该操作的用户必须是该类的拥有者,或者具有DBA权限。
删除类体
从数据库中删除一个类的实现主体对象。
语法格式
DROP CLASS BODY [<模式名>.]<类名>[RESTRICT | CASCADE]; 使用说明
如果被删除的类不属于当前模式,必须在语句中指明模式名。
权限
执行该操作的用户必须是该类的拥有者,或者具有DBA权限。
类的使用
类类型同普通的数据类型一样,可以作为表中列的数据类型,DMSQL程序语句块中变量的数据类型或过程及函数参数的数据类型。
具体使用规则
1.作为表中列类型或其他类成员变量属性的类不能被修改,删除时需要指定CASCADE级联删除类中定义的数据类型,其名称只在类的声明及实现中有效。如果类内的函数的参数或返回值是类内的数据类型,或是进行类内成员变量的复制,需要在DMSQL程序中定义一个结构与之相同的类型。
根据类使用方式的不同,对象可分为变量对象及列对象。变量对象指的是在DMSQL程序语句块中声明的类类型的变量;列对象指的是在表中类类型的列。变量对象可以修改其属性的值而列对象不能。
2.变量对象的实例化
类的实例化通过NEW 表达式调用构造函数完成。
3.变量对象的引用
通过‘=’进行的类类型变量之间的赋值所进行的是对象的引用,并没有复制一个新的对象。
4.变量对象属性访问
可以通过如下方式进行属性的访问。
<对象名>.<属性名>
5.变量对象成员方法调用
成员方法的调用通过以下方式调用:
<对象名>.<成员方法名>(<参数>{,<参数>})
如果函数内修改了对象内属性的值,则该修改生效。
6.列对象的插入
列对象的创建是通过INSERT语句向表中插入数据完成,插入语句中的值是变量对象,插入后存储在表中的数据即为列对象。
7.列对象的复制
存储在表中的对象不允许对对象中成员变量的修改,通过into查询或’=’进行的列到变量的赋值所进行的是对象的赋值,生成了一个与列对象数据一样的副本,在该副本上进行的修改不会影响表中列对象的值。
8.列对象的属性访问
通过如下方式进行属性的访问:
<列名>.<属性名>
9.列对象的方法调用
<列名>.<成员方法名>(<参数>{,<参数>})
列对象方法调用过程中对类型内属性的修改,都是在列对象的副本上进行的,不会影响列对象的值。
应用实例
1. 变量对象的应用实例
SQL> declare 2 type ex_rec_t is record (a int, b int); --使用一个同结构的类型代替类定义的类型 3 rec ex_rec_t; 4 o1 mycls; 5 o2 mycls; 6 begin 7 o1 = new mycls(1,2,3); 8 o2 = o1; --对象引用 9 rec = o2.r; --变量对象的成员变量访问 10 print rec.a; print rec.b; 11 rec = o1.f1(4,5); --成员函数调用 12 print rec.a; print rec.b; 13 print o1.id; --成员变量访问 14 end; 15 / 2 3 4 5 1 DMSQL executed successfully used time: 3.129(ms). Execute id is 109.
2. 列对象的应用实例
表的创建。
SQL> create table tt1(c1 int, c2 mycls); executed successfully used time: 28.302(ms). Execute id is 112.
列对象的创建–插入数据。
SQL> insert into tt1 values(1, mycls(1,2,3)); affect rows 1 used time: 22.639(ms). Execute id is 113. SQL> commit; executed successfully used time: 17.285(ms). Execute id is 114.
列对象的复制及访问。
SQL> declare 2 o mycls; 3 id int; 4 begin 5 select top 1 c2 into o from tt1; --列对象的复制 6 select top 1 c2.id into id from tt1; --列对象成员的访问 7 end; 8 / DMSQL executed successfully used time: 33.518(ms). Execute id is 115.
3. 类继承的应用实例
SQL> CREATE OR REPLACE CLASS cls01 NOT FINAL IS 2 name VARCHAR2(10); 3 MEMBER FUNCTION get_info RETURN VARCHAR2; 4 END; 5 / executed successfully used time: 22.220(ms). Execute id is 116. SQL> CREATE OR REPLACE CLASS cls02 UNDER cls01 IS 2 ID INT; 3 OVERRIDING MEMBER FUNCTION get_info RETURN VARCHAR2; 4 END; 5 / executed successfully used time: 14.072(ms). Execute id is 117.
JAVA CLASS类型
JAVA类的定义类似JAVA语言语法,类中可定义。
JAVA类中可以包括以下内容:
1. 类型定义
在类中可以定义游标、异常,可以声明记录类型、数组类型、结构体类型以及内存索引表等数据类型变量。
2. 属性
类中的成员变量,数据类型可以是标准的数据类型,可以是在类外自定义的特殊数据类型。
3. 成员方法
JAVA类中的成员方法及后文的构造函数包含一个隐含参数,即自身对象,在方法实现中可以通过this或self来访问自身对象,self等价于this。如果不存在重名问题,也可以直接使用对象的属性和方法。
4. 构造函数
构造函数是类内定义及实现的一种特殊的函数,这类函数用于实例化类的对象,构造函数满足以下条件:
1) 函数名和类名相同;
2) 函数没有返回值类型。
构造函数存在以下的约束:
1) 系统为每个类提供两个默认的构造函数,分别为0参的构造函数和全参的构造函数;
2) 0参构造函数的参数个数为0,实例的对象内所有的属性初始化值为NULL;
3) 全参构造函数的参数个数及类型和类内属性的个数及属性相同,按照属性的顺序依次读取参数的值并给属性赋值;
4) 用户可以自定义构造函数,一个类可以有多个构造函数,但每个构造函数的参数个数必须不同;
5) 如果用户自定义了0个参数、或参数个数同属性个数相同的构造函数,则会覆盖相应的默认构造函数。
定义JAVA类
定义通过CREATE JAVA CLASS语句来完成,其语法为:
语法格式
CREATE [OR REPLACE] JAVA [PUBLIC] [ABSTRACT] [FINAL] CLASS<类名> [EXTENDS [<模式名>.]<父类名>] {<类内定义部分> }
<类内定义部分> ::=<类内定义列表>
<类内定义列表> ::=<类内定义>;{<类内定义>;}
<类内定义> ::= [PUBLIC|PRIVATE]<变量定义>|<方法定义>
<变量定义> ::=<变量属性><数据类型><变量名列表> [默认值定义]
<方法定义> ::= [PUBLIC|PRIVATE] [<方法继承属性>] [STATIC]<返回类型><函数名><参数列表> {<实现体> }
<方法继承属性> ::=<重载属性> |
::= ABSTRACT
::= FINAL
<重载属性> ::= OVERRIDE
使用说明
1.类中元素可以以任意顺序出现,其中的对象必须在引用之前被声明。
2.支持对象静态方法声明与调用。可以在方法前添加static保留字,以此指明方法为静态方法。静态方法只能以对象名为前缀调用,而不能在对象实例中调用。
3.支持对象成员方法声明与调用。非STATIC类型的非构造函数方法默认为成员方法。成员方法调用时,需要先实例化,实例化参数值缺省为null。
4. 变量定义还包括游标、异常定义。
5.方法属性是PUBLIC,则访问类时可以访问,如果是PRIVATE属性,则访问类时不可以访问该方法。
6.关于JAVA 类继承,有以下使用限制:
1) JAVA CLASS定义默认可继承,FINAL表示该类不能被继承;
2) 定义子类时必须指定EXTENDS选项;
3) ABSTRACT对象不能为FINAL;
4) ABSTRACT对象不能实例化,但是可以用其子类赋值;
5) 子类对象实例化时,必须对父类和子类的成员变量都赋值,且从父类到子类逐个赋值;
6) 不支持对象的循环继承;
7) 不支持对象的多继承,即一个类只能有一个父类;
8) 不支持父类和子类包含同名变量;
9) 父类和子类可以同名同参,此时子类必须指定OVERRIDE;
10) 方法默认为NOT OVERRIDING,OVERRIDING不能与static一起使用;
11) 父类和子类支持同名不同参(参数个数不同、参数个数相同但类型不同)的方法;
12) 同名且参数个数相同但类型不同时,根据参数类型选择使用的方法;
13) 方法如果声明为ABSTRACT,则不能与FINAL、STATIC一起使用;
14) 如果父类有多个ABSTRACT方法,子类可以只部分重写,但此时子类必须定义为ABSTRACT;
15) ABSTRACT方法不能具有主体;
16) 方法默认为可继承,如果声明为FINAL,则不能被子类重写;
17) 子类可以赋值给父类;
18) 如果父类对应的实例是子类或者子类的孩子,则该父类可以赋值给子类;
19) 可以用ABSTRACT子类对非ABSTRACT父类进行赋值;
20) 子类实例赋值给父类后,调用时使用的是父类方法而不是子类方法;
21) 支持使用super无参方法转换为父类引用;
22) 支持使用this()调用该类构造函数,super()调用父类构造函数;
23) 子类必须有新增成员或方法,不能完全为空。
重编译JAVA类
重新对JAVA类进行编译,如果重新编译失败,则将JAVA类置为禁止状态。
重编功能主要用于检验JAVA类的正确性。
语法格式
COMPILE [DEBUG];
参数
1.<模式名> 指明被重编译的JAVA类所属的模式;
2.
指明被重编译的JAVA类的名字;
3.[DEBUG] 可忽略。
所需权限
执行该操作的用户必须是JAVA类的创建者,或者具有DBA权限。
12.2.3 删除JAVA类
JAVA类的删除通过DROP CLASS完成。
语法格式
DROP CLASS<类名>[RESTRICT | CASCADE];
类的使用
下面列举一个简单的应用实例。在列对象上如何使用JAVA CLASS。
1.创建JAVA CLASS。
SQL> create or replace java class jcls 2 { 3 int a; 4 public static int testAdd2(int a, int b) 5 { //此处创建的是静态STATIC方法 6 return a + b; 7 } 8 public int testAdd3(int a, int b, int c) 9 { //此处创建的是成员方法 10 return a + b +c; 11 } 12 }; 13 / executed successfully used time: 16.964(ms). Execute id is 123.
2. 在列对象中使用JAVA CLASS。
SQL> create table tt2(c1 int, c2 jcls); executed successfully used time: 9.261(ms). Execute id is 124. SQL> insert into tt2 values(jcls.testadd2(1,2),jcls(1)); //静态方法调用 2 / affect rows 1 used time: 1.255(ms). Execute id is 125. SQL> insert into tt2 values(jcls().testadd3(1,2,3),jcls(2)); //成员方法调用之前必须实例化 2 / affect rows 1 used time: 1.023(ms). Execute id is 126.