Oracle PL/SQL 对象类型教程,附示例
PL/SQL 中的对象类型是什么?
面向对象编程尤其适合构建可重用组件和复杂应用程序。它们围绕“对象”而非“动作”进行组织,即程序设计用于与整个对象而不是单个动作进行交互。此概念允许程序员在对象实体级别填充和操作详细信息。
下图描绘了对象类型的一个示例,其中银行账户被视为一个对象实体。对象属性包括保存某些属性值的项,例如在银行账户中;它是账号、银行余额等。而对象方法则描述了计算利率、生成银行对账单等需要完成某些过程的操作。
在 PL/SQL 中,面向对象编程基于对象类型。
对象类型可以表示任何现实世界的实体。我们将在本章中进一步讨论对象类型。
对象类型的组成部分
PL/SQL 对象类型主要包含两个组成部分。
- 属性
- 成员/方法
属性
属性是存储数据的列或字段。每个属性都将映射到定义该属性的处理和存储类型的基本数据类型。属性可以是任何有效的 PL/SQL 数据类型,也可以是另一个对象类型。
成员/方法
成员或方法是在对象类型中定义的过程。它们不用于存储任何数据。它们主要用于在对象类型内定义进程。例如,在填充对象类型之前验证数据。它们在对象类型部分声明,并在对象类型的对象类型正文部分定义。对象类型中的正文部分是可选的。如果没有成员,则对象类型将不包含正文部分。
在 Oracle 中创建对象
对象类型不能在子程序级别创建。它们只能在模式级别创建。一旦在模式中定义了对象类型,就可以在子程序中使用它。可以使用“CREATE TYPE”创建对象类型。类型正文只能在其对象类型创建后创建。
CREATE TYPE<object_type_name> AS OBJECT ( <attribute_l><datatype>, . . ); / CREATE TYPE BODY<object_type_name> AS OBJECT ( MEMBER[PROCEDURE|FUNCTION]<member_name> IS <declarative section> BEGIN <execution part> END; . . ); /
语法说明
- 上述语法显示了具有属性的“OBJECT”和具有方法的“OBJECT-BODY”的创建。
- 方法也可以在对象正文中重载。
对象类型的声明初始化
与其他 PL/SQL 组件一样,在使用它们进行程序设计之前,也需要声明对象类型。
一旦在模式中创建了对象类型,就可以在子程序的声明部分使用它来声明该对象类型的变量。
无论何时在子程序中将变量声明为对象类型,在运行时都会创建一个对象类型的新实例,并且这个新创建的实例可以引用变量名。通过这种方式,单个对象类型可以存储多个值,这些值位于不同的实例下。
DECLARE <variable_name> <object_type_name>; BEGIN . . END; /
语法说明
- 上述语法显示了在声明部分将变量声明为对象类型的声明。
一旦变量在子程序中声明为对象类型,它将是原子性的 NULL,即整个对象本身为 NULL。它需要用值进行初始化才能在程序中使用。它们可以使用构造函数进行初始化。
构造函数是对象的隐式方法,可以引用与对象类型相同的名称。以下语法显示了对象类型的初始化。
DECLARE <variable_name> <object_type_name>; BEGIN <variable_name>:=<object_type_name>(); END; /
语法说明
- 上述语法显示了使用 NULL 值初始化对象类型实例。
- 现在对象本身不是 NULL,因为它已经初始化,但对象中的属性将为 NULL,因为我们没有为这些属性分配任何值。
构造函数
构造函数是对象的隐式方法,可以引用与对象类型相同的名称。每当第一次引用对象时,都会隐式调用此构造函数。
我们也可以使用这些构造函数初始化对象。可以通过在对象类型正文中定义与对象类型同名的成员来显式定义构造函数。
示例 1:在下面的示例中,我们将使用对象类型成员将记录插入 emp 表,值为('RRR', 1005, 20000, 1000)和('PPP', 1006, 20000, 1001)。一旦插入数据,我们将使用对象类型成员显示它。我们还将使用显式构造函数将第二个记录的 manager id 默认设置为 1001。
我们将按以下步骤执行。
- 步骤 1
- 创建对象类型
- 对象类型正文
- 步骤 2:创建匿名块,通过隐式构造函数为 emp_no 1005 调用已创建的对象类型。
- 步骤 3:创建匿名块,通过显式构造函数为 emp_no 1006 调用已创建的对象类型。
步骤 1) 创建对象类型和对象类型正文
CREATE TYPE emp_object AS OBJECT( emp_no NUMBER, emp_name VARCHAR2(50), salary NUMBER, manager NUMBER, CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER, p_emp_name VARCHAR2, p_salary NUMBER) RETURN SELF AS RESULT), MEMBER PROCEDURE insert_records, MEMBER PROCEDURE display_records); /
CREATE OR REPLACE TYPE BODY emp_object AS CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER,p_emp_name VARCHAR2, p_salary NUMBER) RETURN SELF AS RESULT IS BEGIN Dbms_output.put_line(’Constructor fired..'); SELF.emp_no:=p_emp_no;| SELF.emp_name:=p_emp_name; SELF.salary:=p_salary; SELF.managerial:=1001; RETURN; END: MEMBER PROCEDURE insert_records IS BEGIN INSERT INTO emp VALUES(emp_noemp_name,salary,manager); END MEMBER PROCEDURE display_records IS BEGIN Dbms_output.put_line('Employee Name:'||emp_name); Dbms_output.put_line('Employee Number:'||emp_no); Dbms_output.put_line('Salary':'||salary); Dbms_output.put_line('Manager:'||manager); END: END: /
代码解释
- 代码行 1-9:创建 'emp_object' 对象类型,包含 4 个属性和 3 个成员。它包含只有 3 个参数的构造函数的定义。(实际的隐式构造函数将包含与对象类型中存在的属性数量相等的参数数量)
- 代码行 10:创建类型正文。
- 代码行 11-21:定义显式构造函数。将参数值分配给属性,并将 'manager' 属性的值设置为默认值 '1001'。
- 代码行 22-26:在 'insert_records' 成员中,将属性值插入 'emp' 表。
- 代码行 27-34:在 'display_records' 成员中,显示对象类型属性的值。
输出
类型已创建
类型正文已创建
步骤 2) 创建匿名块,通过隐式构造函数为 emp_no 1005 调用已创建的对象类型
DECLARE guru_emp_det emp_object; BEGIN guru_emp_det:=emp_object(1005,’RRR',20000,1000); guru_emp_det.display_records; guru_emp_det.insert_records; COMMIT; END;
代码解释
- 代码行 37-45:使用隐式构造函数插入记录。对构造函数的调用包含实际的属性值数量。
- 代码行 38:将 guru_emp_det 声明为 'emp_object' 的对象类型。
- 代码行 41:语句 'guru_emp_det.display_records' 调用 'diplay_records' 成员函数,并显示属性值
- 代码行 42:语句 'guru_emp_det.insert_records' 调用 'insert_records' 成员函数,并将属性值插入表中。
输出
员工姓名:RRR
员工编号:1005
薪资:20000
经理:1000
步骤 3) 创建匿名块,通过显式构造函数为 emp_no 1006 调用已创建的对象类型
DECLARE guru_emp_det emp_object; BEGIN guru_emp_det:=emp_object(1006,'PPP',20000); guru_emp_det.display_records; guru_emp_det.insert_records; COMMIT; END; /
输出
Employee Name:PPP Employee Number:1006 Salary:20000 Manager:1001
代码解释
- 代码行 46-53:使用显式构造函数插入记录。
- 代码行 46:将 guru_emp_det 声明为 'emp_object' 的对象类型。
- 代码行 50:语句 'guru_emp_det.display_records' 调用 'display_records' 成员函数,并显示属性值
- 代码行 51:语句 'guru_emp_det.insert_records' 调用 'insert_records' 成员函数,并将属性值插入表中。
对象类型的继承
继承属性允许子对象类型访问父对象类型或超类型对象的所有属性和成员。
子对象类型称为继承对象类型,超类型对象类型称为父对象类型。以下语法显示了如何创建父类型和继承类型。
CREATE TYPE <object_type_name_parent> AS OBJECT ( <attribute_l><datatype>, . . )NOT FINAL; /
语法说明
- 上述语法显示了 SUPER 类型的创建。
CREATE TYPE<object_type_name_sub>UNDER<object_type_name_parent> ( <attribute_l><datatype>, . ); /
语法说明
- 上述语法显示了 SUB 类型的创建。它包含来自父对象类型的所有成员和属性。
示例 1:在下面的示例中,我们将使用继承属性为以下记录('RRR', 1007, 20000)插入经理 ID 为 '1002' 的记录。
我们将按以下步骤执行上述程序
- 步骤 1:创建 SUPER 类型。
- 步骤 2:创建 SUB 类型和正文。
- 步骤 3:创建匿名块以调用 SUB 类型。
步骤 1) 创建 SUPER 类型或父类型。
CREATE TYPE emp_object AS OBJECT( emp_no NUMBER, emp_name VARCHAR2(50), salary NUMBER, manager NUMBER, CONSTRUCTOR FUNCTION emp_object(p_emp_no NUMBER,p_emp_name VARCHAR2(50), p_salary NUMBER)RETURN SELF AS RESULT), MEMBER PROCEDURE insert_records, MEMBER PROCEDURE display_records)NOT FINAL; /
代码解释
- 代码行 1-9:创建 'emp_object' 对象类型,包含 4 个属性和 3 个成员。它包含只有 3 个参数的构造函数的定义。它被声明为 'NOT FINAL',因此它是父类型。
步骤 2) 在 SUPER 类型下创建 SUB 类型。
CREATE OR REPLACE TYPE sub_emp_object UNDER emp_object (default_manager NUMBER,MEMBER PROCEDURE insert_default_mgr); / CREATE OR REPLACE TYPE BODY sub_emp_object AS MEMBER PROCEDURE insert_default_mgr IS BEGIN INSERT INTO emp VALUES(emp_no,emp_name:salary,manager): END; END; /
代码解释
- 代码行 10-13:创建 sub_emp_object 作为继承类型,并附加一个属性 'default_manager' 和成员过程声明。
- 代码行 14:为继承的对象类型创建正文。
- 代码行 16-21:定义成员过程,该过程将值从 'SUPER' 对象类型插入“emp”表中,但经理值除外。对于经理值,它使用 'SUB' 类型中的 'default_manager'。
步骤 3) 创建匿名块以调用 SUB 类型
DECLARE guru_emp_det sub_emp_object; BEGIN guru_emp_det:= sub_emp_object(1007,'RRR',20000,1000,1002); guru_emp_det.insert_default_mgr; COMMIT; END; /
代码解释
- 代码行 25:将 guru_emp_det 声明为 'sub_emp_object' 类型。
- 代码行 27:使用隐式构造函数初始化对象。构造函数有 5 个参数(来自 PARENT 类型的 4 个属性和来自 SUB 类型的 2 个属性)。最后一个参数(1002)定义了 default_manager 属性的值
- 代码行 28:调用成员 'insert_default_mgr' 以插入具有构造函数中传递的默认经理 ID 的记录。
PL/SQL 对象的相等性
属于同一对象的对象实例可以进行相等性比较。为此,我们需要对象类型中有一个特殊的 'ORDER' 方法。
'ORDER' 方法应为一个返回数值的函数。它接受两个参数作为输入(第一个参数:self 对象实例的 ID,第二个参数:另一个对象实例的 ID)。
比较两个对象实例的 ID,并以数值形式返回结果。
- 正值表示 SELF 对象实例大于另一个实例。
- 负值表示 SELF 对象实例小于另一个实例。
- 零表示 SELF 对象实例等于另一个实例。
- 如果任何实例为 NULL,则此函数将返回 NULL。
CREATE TYPE BODY<object_type_name_ 1>AS OBJECT ( ORDER MEMBER FUNCTION match(<parameter> object_type_name_ 1) RETURN INTEGER IS BEGIN IF <attribute_name>parameter <attribute_name>THEN RETURN -1; --any negative number will do ELSIF id>c.id THEN RETURN 1; —any positive number will do ELSE RETURN 0; END IF; END; . . ); /
语法说明
- 上述语法显示了需要包含在类型正文中用于相等性检查的 ORDER 函数。
- 此函数的参数应为同一对象类型的实例。
- 上述函数可以调用为“obj_instance_1.match(obj_instance_2)”,并且此表达式将返回数值,其中 obj_instance_1 和 obj_instance_2 是 object_type_name 的实例。
示例 1:在下面的示例中,我们将查看如何比较两个对象。我们将创建两个实例,并将它们之间的 'salary' 属性进行比较。我们将分两步完成。
- 步骤 1:创建对象类型和正文。
- 步骤 2:创建匿名块以调用比较对象实例。
步骤 1) 创建对象类型和正文。
CREATE TYPE emp_object_equality AS OBJECT( salary NUMBER, ORDER MEMBER FUNCTION equals(c emp_object_equality)RETURN INTEGER); /
CREATE TYPE BODY emp_object_equality AS ORDER MEMBER FUNCTION equals(c emp_object_equality)RETURN INTEGER IS BEGIN IF salary<c.salary THEN RETURN -1; ELSIF salary>c.salary THEN RETURN 1; ELSE RETURN 0; END IF: END; END; /
代码解释
- 代码行 1-4:创建 'emp_object_equality' 对象类型,包含 1 个属性和 1 个成员。
- 代码行 6-16:定义 ORDER 函数,该函数比较 SELF 实例和参数类型的 'salary' 属性。如果 SELF 的薪资较低,则返回负数;如果 SELF 的薪资较高,则返回正数;如果薪资相等,则返回 0。
代码输出
类型已创建
步骤 2) 创建匿名块以调用比较对象实例。
DECLARE l_obj_l emp_object_equality; l_obj_2 emp_object_equality; BEGIN l_obj_l:=emp_object_equality(15000); l_obj_2:=emp_object_equality(17000); IF l_obj_1.equalS(l_obj_2)>0 THEN Dbms_output.put_line(’Salary of first instance is greater’): ELSIF l_obj_l.equalS(l_obj_2)<0 THEN Dbms_output.put_line(’Salary of second instance is greater’); ELSE Dbms_output.put_line(’Salaries are equal’); END IF; END; /
输出
Salary of second instance is greater
代码解释
- 代码行 20:将 l_obj_1 声明为 emp_object_equality 类型。
- 代码行 21:将 l_obj_2 声明为 emp_object_equality 类型。
- 代码行 23:用薪资值 '15000' 初始化 l_obj_1
- 代码行 24:用薪资值 '17000' 初始化 l_obj_1
- 代码行 25-33:根据 ORDER 函数的返回数字打印消息。
摘要
在本章中,我们了解了对象类型及其属性。我们还讨论了 PL/SQL 对象中的构造函数、成员、属性、继承和相等性。