类类型

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

图例

申明类

图 12.1 申明类.png

使用说明

  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] 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 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 应用实例

下面列举一个简单的应用实例。在列对象上如何使用普通 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 类

定义 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 类

图 12.6 重编译 JAVA 类.png

权限

执行该操作的用户必须是 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));	//成员方法调用之前必须实例化

12.3 使用规则

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

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

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

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

2. 变量对象的实例化

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

3. 变量对象的引用

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

4. 变量对象属性访问

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

  <对象名>.<属性名>

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

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

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

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

6. 列对象的插入

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

7. 列对象的复制

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

8. 列对象的属性访问

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

  <列名>.<属性名>

9. 列对象的方法调用

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

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

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