类类型

DM通过类类型在DMSQL程序中实现面向对象编程的支持。类将结构化的数据及对其进行操作的过程或函数封装在一起。允许用户根据现实世界的对象建模,而不必再将其抽象成关系数据。

DM的类类型分为普通类类型和JAVA CLASS类型。DM文档中的示例除了特别声明使用的是JAVA CLASS类型,要不然使用的都是普通类类型。

12.1 普通CLASS类型

DM的类的定义分为类头和类体两部分,类头完成类的声明;类体完成类的实现。

类中可以包括以下内容:

1. 类型定义

在类中可以定义游标、异常、记录类型、数组类型、以及内存索引表等数据类型,在类的声明及实现中可以使用这些数据类型;类的声明中不能声明游标和异常,但是实现中可以定义和使用。

2. 属性

类中的成员变量,数据类型可以是标准的数据类型,可以是在类中自定义的特殊数据类型。

3. 成员方法

类中的函数或过程,在类头中进行声明;其实现在类体中完成;

成员方法及后文的构造函数包含一个隐含参数,即自身对象,在方法实现中可以通过this或self来访问自身对象,self等价于this。如果不存在重名问题,也可以直接使用对象的属性和方法。this和self只能在包或对象脚本中调用。

4. 构造函数

构造函数是类内定义及实现的一种特殊的函数,这类函数用于实例化类的对象,构造函数满足以下条件:

  1. 函数名和类名相同;
  2. 函数返回值类型为自身类。

构造函数存在以下的约束:

  1. 系统为每个类提供两个默认的构造函数,分别为0参的构造函数和全参的构造函数;
  2. 0参构造函数的参数个数为0,实例的对象内所有的属性初始化值为NULL;
  3. 全参构造函数的参数个数及类型和类内属性的个数及属性相同,按照属性的顺序依次读取参数的值并给属性赋值;
  4. 用户可以自定义构造函数,一个类可以有多个构造函数,但每个构造函数的参数个数必须不同;
  5. 如果用户自定义了0个参数、或参数个数同属性个数相同的构造函数,则会覆盖相应的默认构造函数。

下面从类的声明、类的实现、类的删除、类体的删除和类的使用几部分来详细介绍类类型的实现过程。

12.1.1 声明类

类的声明在类头中完成。类头定义通过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]

  <方法继承属性> ::= <重载属性> | <final属性> | <重载属性> <final属性>

  <重载属性> ::= [NOT] OVERRDING

  <final属性> ::= 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. 不支持对象的多继承,即一个类有多个父类;
    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则为当前用户权限,默认为类定义者权限。

12.1.2 实现类

类的实现通过类体完成。类体的定义通过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 [函数名]

  <方法继承属性> ::= <重载属性> | <final属性> | <重载属性> <final属性>

  <重载属性> ::= [NOT] OVERRDING

  <final属性> ::= FINAL | NOT FINAL | INSTANTIABLE | NOT INSTANTIABLE

  <初始化代码> ::= [[<说明部分>]BEGIN<执行部分>[<异常处理部分>]]

  <说明部分> ::=[DECLARE]<说明定义>{<说明定义>}

  <说明定义>::=<变量说明>|<异常变量说明>|<游标定义>|<子过程定义>|<子函数定义>

  <变量说明>::=<变量名>{,<变量名>}<变量类型>[DEFAULT|ASSIGN|:=<表达式>];

  <变量类型>::=<DMSQL程序类型> | [<模式名>.]<表名>.<列名>%TYPE | [<模式名>.]<表名>%ROWTYPE | <记录类型>

  <记录类型>::= RECORD(<变量名> <DMSQL程序类型>{,<变量名> <DMSQL程序类型>})

  <异常变量说明>::=<异常变量名>EXCEPTION[FOR<错误号>]

  <游标定义>::=CURSOR <游标名> [FOR<查询表达式>|<表连接>]

  <子过程定义>::=PROCEDURE<过程名>[(<参数列>)] IS|AS <模块体>

  <子函数定义>::=FUNCTION<函数名>[(<参数列>)]RETURN<返回数据类型> [PIPELINED] IS|AS <模块体>

  <执行部分>::=<SQL过程语句序列>{;< SQL过程语句序列>}

  <SQL过程语句序列>::=[<标号说明>]<SQL过程语句>;

  <标号说明>::=<<<标号名>>>

  <SQL过程语句>::=<SQL语句>|<SQL控制语句>

  <异常处理部分>::=EXCEPTION<异常处理语句>{;<异常处理语句>};

  <异常处理语句>::= WHEN <异常名> THEN < SQL过程语句序列>

图例

实现类

实现类

使用说明

  1. 类声明中定义的对象对于类体而言都是可见的,不需要声明就可以直接引用。这些对象包括变量、游标、异常定义和类型定义;
  2. 类体中的过程、函数定义必须和类声明中的声明完全相同。包括过程的名字、参数定义列表的参数名和数据类型定义;
  3. 类中可以有重名的成员方法,要求其参数定义列表各不相同。系统会根据用户的调用情况进行重载(Overload);
  4. 声明类与实现类时,对于确定性函数的指定逻辑与包内函数相同。目前不支持类的确定性函数在函数索引中使用。

权限

使用该语句的用户必须是DBA或该类对象的拥有者且具有CREATE CLASS数据库权限的用户。

完整的类头、类体的创建如下所示:

----类头创建

create class mycls

as

type rec_type is record (c1 int, c2 int); --类型声明

id		int; 							--成员变量

r			rec_type; 						--成员变量

function f1(a int, b int) return rec_type; --成员函数

function mycls(id int , r_c1 int, r_c2 int) return mycls;

--用户自定义构造函数

end;

/

----类体创建

create or replace class body mycls

as

function f1(a int, b int) return rec_type

as

begin

r.c1 = a;

r.c2 = b;

return r;

end;

function mycls(id int, r_c1 int, r_c2 int) return mycls

as

begin

this.id = id;			--可以使用this.来访问自身的成员

r.c1 = r_c1;			--this也可以省略

r.c2 = r_c2;

return this;			--使用return this 返回本对象

end;

end;

/

12.1.3 重编译类

重新对类进行编译,如果重新编译失败,则将类置为禁止状态。

重编功能主要用于检验类的正确性。

语法格式

  ALTER CLASS [<模式名>.]<类名> COMPILE [DEBUG];

参数

1.<模式名> 指明被重编译的类所属的模式;

2.<类名> 指明被重编译的类的名字;

3.[DEBUG] 可忽略。

图例

重编译类

重编译类

权限

执行该操作的用户必须是类的创建者,或者具有DBA权限。

举例说明

例 重新编译类

  ALTER CLASS mycls COMPILE;

12.1.4 删除类

类的删除分为两种方式:一是类头的删除,删除类头则会顺带将类体一起删除;另外一种是类体的删除,这种方式只能删除类体,类头依然存在。

12.1.4.1 删除类头

类的删除通过DROP CLASS完成,即类头的删除。删除类头的同时会一并删除类体。

语法格式

  DROP CLASS [IF EXISTS] [<模式名>.]<类名>[RESTRICT | CASCADE];

使用说明

  1. 删除不存在的类头会报错。若指定IF EXISTS关键字,删除不存在的类头,不会报错;
  2. 如果被删除的类不属于当前模式,必须在语句中指明模式名;
  3. 如果一个类的声明被删除,那么对应的类体被自动删除。

权限

执行该操作的用户必须是该类的拥有者,或者具有DBA权限。

12.1.4.2 删除类体

从数据库中删除一个类的实现主体对象。

语法格式

  DROP CLASS BODY [IF EXISTS] [<模式名>.]<类名>[RESTRICT | CASCADE];

使用说明

  1. 删除不存在的类体会报错。若指定IF EXISTS关键字,删除不存在的类体,不会报错;
  2. 如果被删除的类不属于当前模式,必须在语句中指明模式名。

权限

执行该操作的用户必须是该类的拥有者,或者具有DBA权限。

12.1.5 类的使用

类类型同普通的数据类型一样,可以作为表中列的数据类型,DMSQL程序语句块中变量的数据类型或过程及函数参数的数据类型。

12.1.5.1 具体使用规则

1. 作为表中列类型或其他类成员变量属性的类不能被修改,删除时需要指定CASCADE级联删除

类中定义的数据类型,其名称只在类的声明及实现中有效。如果类内的函数的参数或返回值是类内的数据类型,或是进行类内成员变量的复制,需要在DMSQL程序中定义一个结构与之相同的类型。

根据类使用方式的不同,对象可分为变量对象及列对象。变量对象指的是在DMSQL程序语句块中声明的类类型的变量;列对象指的是在表中类类型的列。变量对象可以修改其属性的值而列对象不能。

2. 变量对象的实例化

类的实例化通过NEW 表达式调用构造函数完成。

3. 变量对象的引用

通过‘=’进行的类类型变量之间的赋值所进行的是对象的引用,并没有复制一个新的对象。

4. 变量对象属性访问

可以通过如下方式进行属性的访问。

  <对象名>.<属性名>

5. 变量对象成员方法调用

成员方法的调用通过以下方式调用:

  <对象名>.<成员方法名>(<参数>{,<参数>})

如果函数内修改了对象内属性的值,则该修改生效。

6. 列对象的插入

列对象的创建是通过INSERT语句向表中插入数据完成,插入语句中的值是变量对象,插入后存储在表中的数据即为列对象。

7. 列对象的复制

存储在表中的对象不允许对对象中成员变量的修改,通过into查询或’=’进行的列到变量的赋值所进行的是对象的赋值,生成了一个与列对象数据一样的副本,在该副本上进行的修改不会影响表中列对象的值。

8. 列对象的属性访问

通过如下方式进行属性的访问:

  <列名>.<属性名>

9. 列对象的方法调用

  <列名>.<成员方法名>(<参数>{,<参数>})

列对象方法调用过程中对类型内属性的修改,都是在列对象的副本上进行的,不会影响列对象的值。

12.1.5.2 应用实例

  1. 变量对象的应用实例
  declare

	type ex_rec_t is record (a int, b int); --使用一个同结构的类型代替类定义的类型

	rec ex_rec_t;

	o1 mycls;

	o2 mycls;

  begin

	o1 = new mycls(1,2,3);

	o2 = o1; --对象引用

	rec = o2.r; --变量对象的成员变量访问

	print rec.a; print rec.b;

	rec = o1.f1(4,5); --成员函数调用

	print rec.a; print rec.b;

	print o1.id; --成员变量访问

  end;
  1. 列对象的应用实例

表的创建。

  Create table tt1(c1 int, c2 mycls);

列对象的创建--插入数据。

  Insert into tt1 values(1, mycls(1,2,3));

列对象的复制及访问。

  Declare

	o mycls;

	id int;

  begin

	select top 1 c2 into o from tt1; 		--列对象的复制

	select top 1 c2.id into id from tt1; 	--列对象成员的访问

  end;
  1. 类继承的应用实例
  CREATE OR REPLACE CLASS cls01 NOT FINAL IS

	name VARCHAR2(10);

	MEMBER FUNCTION get_info RETURN VARCHAR2;

  END;

  CREATE OR REPLACE CLASS cls02 UNDER cls01 IS

	ID INT;

	OVERRIDING MEMBER FUNCTION get_info RETURN VARCHAR2;

  END;

12.2 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个参数、或参数个数同属性个数相同的构造函数,则会覆盖相应的默认构造函数。

12.2.1 定义JAVA类

定义通过CREATE JAVA CLASS语句来完成,其语法为:

语法格式

  CREATE [OR REPLACE] JAVA [PUBLIC] [ABSTRACT] [FINAL] CLASS <类名> [EXTENDS [<模式名>.]<父类名>] {<类内定义部分> }

  <类内定义部分> ::= <类内定义列表>

  <类内定义列表> ::= <类内定义>;{<类内定义>;}

  <类内定义> ::= [PUBLIC|PRIVATE] <变量定义>|<方法定义>

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

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

  <方法定义> ::= [PUBLIC|PRIVATE] [<方法继承属性>] [STATIC] <返回类型> <函数名><参数列表> { <实现体> }

  <方法继承属性> ::= <重载属性> | <FINAL属性> | <ABSTRACT属性>

  <ABSTRACT属性> ::= ABSTRACT

  <FINAL属性> ::= FINAL

  <重载属性> ::= OVERRIDE

图例

定义JAVA类

定义JAVA类

<类内定义部分>

类内定义部分

使用说明

  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. 子类必须有新增成员或方法,不能完全为空。

12.2.2 重编译JAVA类

重新对JAVA类进行编译,如果重新编译失败,则将JAVA类置为禁止状态。

重编功能主要用于检验JAVA类的正确性。

语法格式

  ALTER JAVA CLASS [<模式名>.]<JAVA类名> COMPILE [DEBUG];

参数

1.<模式名> 指明被重编译的JAVA类所属的模式;

2.<JAVA类名> 指明被重编译的JAVA类的名字;

3.[DEBUG] 可忽略。

图例

重编译类

重编译JAVA类

权限

执行该操作的用户必须是JAVA类的创建者,或者具有DBA权限。

12.2.3 删除JAVA类

JAVA类的删除通过DROP CLASS完成。

语法格式

  DROP CLASS [IF EXISTS] <类名>[RESTRICT | CASCADE];

12.2.4 类的使用

下面列举一个简单的应用实例。在列对象上如何使用JAVA CLASS。

1.创建JAVA CLASS。

  create or replace java class jcls

  {

	int a;

	public static int testAdd2(int a, int b) { //此处创建的是静态STATIC方法

		return a + b;

	  }

	public int testAdd3(int a, int b, int c) { //此处创建的是成员方法

		return a + b +c;

	  }

  }
  1. 在列对象中使用JAVA CLASS。
  create table tt2(c1 int, c2 jcls);

  insert into tt2 values(jcls.testadd2(1,2),jcls(1));	//静态方法调用

  insert into tt2 values(jcls().testadd3(1,2,3),jcls(2));	//成员方法调用之前必须实例化
微信扫码
分享文档
扫一扫
联系客服