类类型

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 [IF NOT EXISTS] [<模式名>.]<类名> [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] OVERRIDING
  <final属性> ::= FINAL | NOT FINAL | INSTANTIABLE | NOT INSTANTIABLE
  <类型声明> ::= TYPE <类型名称> IS <数据类型>

图例

声明类

声明类.png

使用说明

  1. 类的名称不能与系统创建的模式名称相同;

  2. 类中元素可以以任意顺序出现,其中的对象必须在引用之前被声明;

  3. 过程和函数的声明都是前向声明,类声明中不包括任何实现代码;

  4. 支持对象静态方法声明与调用。可以在 PROCEDURE/FUNCTION 关键字前添加 static 保留字,以此指明方法为静态方法。静态方法只能以对象名为前缀调用,而不能在对象实例中调用;

  5. 支持对象成员方法声明与调用。可以在 PROCEDURE/FUNCTION 关键字前添加 MEMBER 以指明方法为成员方法。MEMBER 与 STATIC 不能同时使用,非 STATIC 类型的非构造函数方法默认为成员方法。MAP 表示将对象类型的实例映射为标量数值,只能用于成员类型的 FUNCTION;

  6. 关于类继承,有以下使用限制:

    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 PACKAGE 数据库权限的用户;

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] OVERRIDING
  <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过程语句序列>

图例

实现类

图 12.1 实现类.png

使用说明

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

权限

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

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

//类头创建
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,或者是该类的拥有者且具有 DROP PACKAGE 权限。

12.1.4.2 删除类体

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

语法格式

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

使用说明

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

权限

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

12.1.5 应用实例

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

  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 类

图 12.4 定义 JAVA 类.png

< 类内定义部分 >(class_inner_define_part)

图 12.5 类内定义部分.png

使用说明

  1. 类的名称不能与系统创建的模式名称相同;
  2. 类中元素可以以任意顺序出现,其中的对象必须在引用之前被声明;
  3. 支持对象静态方法声明与调用。可以在方法前添加 static 保留字,以此指明方法为静态方法。静态方法只能以对象名为前缀调用,而不能在对象实例中调用;
  4. 支持对象成员方法声明与调用。非 STATIC 类型的非构造函数方法默认为成员方法。成员方法调用时,需要先实例化,实例化参数值缺省为 null;
  5. 变量定义还包括游标、异常定义。使用游标时需要在 OPEN、FETCH 及 CLOSE 后使用“CURSOR”关键字;
  6. 方法属性是 PUBLIC,则访问类时可以访问,如果是 PRIVATE 属性,则访问类时不可以访问该方法;
  7. 关于 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. 子类必须有新增成员或方法,不能完全为空。

权限

使用该语句的用户必须是 DBA 或具有 CREATE PACKAGE 数据库权限的用户。

12.2.2 重编译 JAVA 类

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

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

语法格式

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

参数

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

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

3.[DEBUG] 可忽略。

图例

重编译 JAVA 类

图 12.6 重编译 JAVA 类.png

权限

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

12.2.3 删除 JAVA 类

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

语法格式

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

参数

1.< 模式名 > 指明待删除的 JAVA 类所属的模式,可忽略;

2.<JAVA 类名 > 指明待删除的 JAVA 类的名字。

图例

删除 JAVA 类

删除 JAVA 类.png

使用说明

删除不存在的 JAVA 类会报错。若指定 IF EXISTS 关键字,删除不存在的 JAVA 类,不会报错。

权限

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

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));	//成员方法调用之前必须实例化

12.3 使用规则

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

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

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

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

2. 变量对象的实例化

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

3. 变量对象的引用

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

4. 变量对象属性访问

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

  <对象名>.<属性名>

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

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

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

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

6. 列对象的插入

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

7. 列对象的复制

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

8. 列对象的属性访问

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

  <列名>.<属性名>

9. 列对象的方法调用

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

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

微信扫码
分享文档
扫一扫
联系客服