类类型

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] < 变量定义>|< 方法定义>

< 变量定义> ::= < 变量属性> < 数据类型>< 变量名列表> [默认值定义]

< 变量属性> ::= [STATIC]

< 方法定义> ::= [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类的正确性。

语法格式

ALTER JAVA CLASS [< 模式名>.] 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.