Oracle PL/SQL 对象类型:CREATE TYPE 示例

⚡ 智能摘要

PL/SQL 中的对象类型将面向对象编程引入到 Oracle允许您使用属性和方法对现​​实世界的实体进行建模。它们支持构造函数、继承和相等性比较,因此单个模式级类型可以存储和处理结构化数据。

  • 🧱 对象类型组件: 对象类型结合了存储数据的属性和定义其处理逻辑的成员或方法。
  • ????️ 创建对象: 对象类型是在模式级别使用 CREATE TYPE 创建的,它们的方法是在单独的 CREATE TYPE BODY 中定义的。
  • 🔧 构造函数: 每个对象类型都有一个以该类型命名的隐式构造函数,你也可以定义一个显式构造函数来设置默认值。
  • 🧬 遗产: NOT FINAL 父类型通过 UNDER 扩展,因此子类型继承所有父属性和成员。
  • 平等: ORDER 成员函数比较两个对象实例,并返回负数、零或正数。
  • 🤖 人工智能协助: GitHub Copilot 等 AI 助手可以根据注释草拟 CREATE TYPE 定义、构造函数和 ORDER 方法。

Oracle PL/SQL 对象类型,显示带有属性和方法的 CREATE TYPE 语句

PL/SQL 中的对象类型是什么?

面向对象编程尤其适合构建可重用组件和复杂应用程序。程序围绕“对象”而非“动作”组织;也就是说,它们的设计目标是与整个对象交互,而不是与单个动作交互。这种方法允许程序员在对象实体级别填充和操作细节。

下图展示了一个对象类型的示例,其中银行账户被视为一个对象实体。对象属性保存值——例如,银行账户的属性包括账号、银行余额等等——而对象方法则描述诸如计算利率或生成银行对账单之类的操作,每个操作都需要完成一个流程。

在 PL/SQL 中,银行账户被建模为具有属性和方法的对象类型。

In PL / SQL面向对象编程基于对象类型。对象类型可以代表任何现实世界的实体。以下章节将探讨对象类型、它们的组成部分以及如何创建和使用它们。

对象类型的组成部分

PL/SQL 对象类型主要包含两个组成部分。

  1. Attributes
  2. 成员/方法

Attributes

属性是存储数据的列或字段。每个属性都映射到一个数据类型,该数据类型定义了该属性的处理和存储方式。属性可以是任何有效的数据类型。 PL/SQL 数据类型,也可以是其他对象类型。

成员/方法

成员或方法是 子程序 这些成员定义在对象类型内部。它们不用于存储数据;相反,它们定义了对象类型内部执行的处理过程——例如,在填充对象之前验证数据。它们在对象类型规范中声明,并在对象类型主体中定义。主体是可选的:如果没有成员,则对象类型没有主体部分。

在中创建对象 Oracle

对象类型不能在子程序级别创建,只能在模式级别创建。对象类型在模式中定义后,即可在子程序中使用。对象类型使用“CREATE TYPE”语句创建,类型体只能在其对象类型存在之后才能创建。

下面的屏幕截图显示了对象规范的 CREATE TYPE 语法和定义其方法的 CREATE TYPE BODY 语法。

CREATE TYPE 语法定义了一个带有属性的对象规范 Oracle CREATE TYPE BODY 语法定义了 PL/SQL 对象类型的方法

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 中的其他组件一样,对象类型必须先声明才能在程序中使用。对象类型创建完成后,即可在子程序的声明部分中使用它来声明该对象类型的变量。

每当一个变量被声明为对象类型时,运行时都会创建一个新的对象类型实例,而这个新创建的实例就通过变量名来引用。这样,同一个对象类型就可以在不同的实例下存储多个值。

下面的屏幕截图显示了在内部将变量声明为对象类型的情况。 PL/SQL 块.

在 PL/SQL 块声明部分中声明对象类型的变量

DECLARE
<variable_name> <object_type_name>;
BEGIN
.
.
END;
/

语法解释:

  • 上面的语法显示了一个声明。 变量 作为声明部分的对象类型。

在子程序中,一旦变量被声明为对象类型,它就原子性地为空——整个对象本身就是空的。必须先赋予它值,才能在程序中使用。对象通过构造函数进行初始化。

构造函数是对象的隐式方法,可以使用与对象类型相同的名称来引用它。下面的屏幕截图显示了对象类型实例的初始化过程。

使用构造函数将对象类型实例初始化为 null

DECLARE
<variable_name> <object_type_name>; 
BEGIN
<variable_name>:=<object_type_name>();
END;
/

语法解释:

  • 上面的语法显示了使用空值初始化对象类型实例的方法。
  • 对象本身在初始化后不再为空,但对象内部的属性在被赋值之前仍为空。

构造函数

构造函数是对象的隐式方法,可以用与对象类型相同的名称来引用它。每当首次引用该对象时,都会隐式调用此构造函数。

您还可以使用这些构造函数初始化对象。构造函数可以通过在对象类型主体中定义一个与对象类型同名的成员来显式定义。

例如1: 在以下示例中,我们使用对象类型成员向 emp 表中插入两条记录,其值为 ('RRR', 1005, 20000, 1000) 和 ('PPP', 1006, 20000, 1001)。数据插入后,我们使用对象类型成员显示它。此外,我们还使用显式构造函数将第二条记录的经理 ID 填充为默认值 1001。

我们按以下步骤执行。

  • 步骤 1:创建对象类型和对象类型主体。
  • 步骤 2:创建一个匿名块,通过 emp_no 1005 的隐式构造函数调用对象类型。
  • 步骤 3:创建一个匿名块,通过显式构造函数为 emp_no 1006 调用对象类型。

步骤 1)创建对象类型和对象类型主体。

下面的屏幕截图显示了正在创建的 emp_object 对象类型规范及其属性和成员。

创建包含属性和成员的 emp_object 对象类型规范

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);
/

下面的屏幕截图显示了使用显式构造函数和成员过程创建的 emp_object 类型主体。

使用显式构造函数和成员过程定义 emp_object 类型主体。

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.manager:=1001;
RETURN;
END;
MEMBER PROCEDURE insert_records
IS
BEGIN
INSERT INTO emp VALUES(emp_no,emp_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;
/

Code 说明

  • Code 第 1-9 行: 创建具有 4 个属性和 3 个成员的“emp_object”对象类型。它包含一个仅具有 3 个参数的构造函数定义。(实际的隐式构造函数包含的参数数量等于对象类型中存在的属性数量。)
  • Code 第10行: 创建文本主体。
  • Code 第 11-21 行: 定义显式构造函数。将参数值分配给属性,并将属性“manager”的默认值设置为“1001”。
  • Code 第 22-26 行: 定义成员“insert_records”,其中属性值将插入到“emp”表中。
  • Code 第 27-34 行: 定义成员“display_records”,用于显示对象类型属性的值。

输出:

创建类型

类型主体已创建

步骤2) 创建一个匿名块,通过隐式构造函数为 emp_no 1005 调用创建的对象类型。

下面的屏幕截图显示了通过隐式构造函数调用 emp_object 的匿名块。

匿名代码块通过 emp_no 1005 的隐式构造函数调用 emp_object

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;

Code 说明

  • Code 第 37-45 行: 使用隐式构造函数插入记录。构造函数调用中包含属性值的实际数量。
  • Code 第38行: 声明“guru_emp_det”为对象类型“emp_object”。
  • Code 第41行: 语句“guru_emp_det.display_records”调用“display_records”成员,并显示属性值。
  • Code 第42行: 语句“guru_emp_det.insert_records”调用“insert_records”成员,并将属性值插入到表中。然后,该代码块提交更改。 交易.

输出:

员工姓名:RRR

员工数量:1005

薪水:S$20000

经理:1000

步骤3) 创建一个匿名块,通过显式构造函数为 emp_no 1006 调用创建的对象类型。

下面的屏幕截图显示了匿名块,该块通过带有三个参数的显式构造函数调用 emp_object。

匿名块通过显式构造函数调用 emp_no 1006 的 emp_object。

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

Code 说明:

  • Code 第 46-53 行: 使用显式构造函数插入记录。
  • Code 第46行: 声明“guru_emp_det”为对象类型“emp_object”。
  • Code 第50行: 语句“guru_emp_det.display_records”调用“display_records”成员,并显示属性值。
  • Code 第51行: 语句“guru_emp_det.insert_records”调用“insert_records”成员,并将属性值插入到表中。由于只传递了三个参数,显式构造函数将经理 ID 设置为其默认值 1001。

对象类型的继承

继承属性允许子对象类型访问父对象类型的所有属性和成员。

子对象类型称为继承对象类型,父对象类型称为父对象类型。以下语法展示了如何创建父对象类型和继承对象类型。下面的屏幕截图显示了标记为 NOT FINAL 的父 (SUPER) 类型语法。

用于继承的非最终父对象类型的 CREATE TYPE 语法

CREATE TYPE <object_type_name_parent> AS OBJECT
(
<attribute_l><datatype>,
.
.
)NOT FINAL;
/

语法解释:

  • 上面的语法展示了如何创建 SUPER 类型。NOT FINAL 子句允许该类型被继承。

下面的屏幕截图显示了使用 UNDER 关键字创建的继承 (SUB) 类型语法。

CREATE TYPE UNDER 语法用于创建继承自父对象的子对象类型。

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 类型或父类型。

下面的屏幕截图显示了使用 NOT FINAL 子句创建的 emp_object 超类型。

创建继承示例中的 emp_object NOT FINAL 超类型

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;
/

Code 说明:

  • Code 第 1-9 行: 创建了具有 4 个属性和 3 个成员的“emp_object”对象类型。它包含一个只有 3 个参数的构造函数定义。它已被声明为“NOT FINAL”,因此它是一个父类型。

步骤 2)在 SUPER 类型下创建 SUB 类型。

下面的屏幕截图显示了子对象继承类型及其主体的创建过程。

使用 insert_default_mgr 创建子对象继承类型及其主体

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;
/

Code 说明:

  • Code 第 10-13 行: 创建继承类型“sub_emp_object”,并添加一个属性“default_manager”和一个成员过程声明。
  • Code 第14行: 创建继承对象类型的主体。
  • Code 第 16-21 行: 定义成员过程,该过程将记录插入到“emp”表中,记录值来自SUPER对象类型,但经理值除外。对于经理值,它使用SUB类型中的“default_manager”。

步骤 3)创建匿名块来调用 SUB 类型。

下面的屏幕截图显示了调用 SUB 类型并插入默认管理器 ID 的匿名块。

匿名块调用 sub_emp_object 类型以插入默认经理 ID

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;
/

Code 说明:

  • Code 第25行: 将“guru_emp_det”声明为“sub_emp_object”类型。
  • Code 第27行: 使用隐式构造函数初始化对象。该构造函数有 5 个参数(4 个来自 PARENT 类型的属性和 1 个来自 SUB 类型的属性)。最后一个参数 (1002) 定义了 default_manager 属性的值。
  • Code 第28行: 调用成员函数“insert_default_mgr”插入具有构造函数中传入的默认经理 ID 的记录。

PL/SQL 对象的相等性

可以比较属于同一对象类型的对象实例是否相等。为此,该对象类型需要一个名为 ORDER 方法的特殊方法。

这个 ORDER 方法应该是一个返回数值类型的函数。它接受两个参数作为输入(第一个参数:自身对象实例的 ID;第二个参数:另一个对象实例的 ID)。

比较两个对象实例的 ID,并将结果以数字形式返回。

  • 正值表示 SELF 对象实例大于其他实例。
  • 负值表示 SELF 对象实例小于另一个实例。
  • 零表示 SELF 对象实例与其他实例相等。
  • 如果其中任何一个实例为空,则函数返回空值。

下面的屏幕截图显示了 ORDER 成员函数语法,该语法必须包含在类型主体中才能进行相等性检查。

ORDER 成员函数语法用于比较对象实例是否相等

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 是对象类型的实例。

例如1: 在以下示例中,我们将比较两个对象。我们创建两个实例,并比较它们的“salary”属性。我们分两步完成此操作。

  • 步骤 1:创建对象类型和主体。
  • 步骤 2:创建匿名块来比较对象实例。

步骤 1)创建对象类型和主体。

下面的屏幕截图显示了 emp_object_equality 规范及其主体,其中定义了比较 salary 属性的 ORDER 函数。

使用 ORDER 函数创建 emp_object_equality 对象类型规范 定义 emp_object_equality 类型主体,用于比较 salary 属性

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;
/

Code 说明:

  • Code 第 1-4 行: 创建具有 1 个属性和 1 个成员的“emp_object_equality”对象类型。
  • Code 第 6-16 行: 定义 ORDER 函数,该函数比较 SELF 实例和参数实例的“salary”属性。如果 SELF 的薪资低于参数实例的薪资,则返回负值;如果 SELF 的薪资高于参数实例的薪资,则返回正值;如果两者的薪资相等,则返回 0。

Code 输出:

创建类型

步骤 2)创建匿名块以比较对象实例。

下面的屏幕截图显示了匿名块,该块按薪水比较两个 emp_object_equality 实例。

匿名块比较两个 emp_object_equality 实例的薪资。

DECLARE
l_obj_1 emp_object_equality;
l_obj_2 emp_object_equality;
BEGIN
l_obj_1:=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_1.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

Code 说明:

  • Code 第20行: 声明类型为“emp_object_equality”的“l_obj_1”。
  • Code 第21行: 声明类型为“emp_object_equality”的“l_obj_2”。
  • Code 第23行: 使用薪资值“15000”初始化“l_obj_1”。
  • Code 第24行: 使用薪资值“17000”初始化“l_obj_2”。
  • Code 第 25-33 行: 根据 ORDER 函数返回的数字打印消息。

常见问题

对象类型对具有属性和方法的单个现实世界实体进行建模。 采集 — 可变数组、嵌套表或关联数组 — 可以存储多个相同数据类型的元素。对象类型描述结构和行为,而集合则存储多个值。

两者都比较对象实例。MAP 方法返回一个标量值。 Oracle `sort` 方法用于对对象进行排序或比较,而 `ORDER` 方法则直接比较两个实例并返回负数、零或正数。一个类型可以定义其中一种,但不能同时定义两种。

是的。您可以创建一个对象表,其中每一行都是一个对象实例,或者使用对象类型作为表列的数据类型。这样就可以…… Oracle 将结构化对象直接持久化到数据库中,而不是仅仅保存在内存中。

非实例化类型不能直接创建对象;它充当绝对值。tract 是其他类型所继承的基础类型。结合 NOT FINAL,它定义了一个超类型,其子类型提供了已声明方法的具体实现。

使用 ALTER TYPE 语句可以添加或删除属性和方法,添加 CASCADE 语句可以更新依赖对象,而使用 DROP TYPE 语句可以删除类型。如果某个类型仍被表或其他类型使用,则必须先处理这些依赖对象,才能删除该类型。

查询数据字典视图 USER_TYPES 和 ALL_TYPES 获取类型名称,查询 USER_TYPE_ATTRS 和 USER_TYPE_METHODS 获取其属性和方法。这些视图允许您在不阅读源代码的情况下审核现有对象模型。

是的。 GitHub 副驾驶 从注释中草拟 CREATE TYPE 规范、TYPE BODY 方法、构造函数和 ORDER 函数。 Rev在部署生成的类型之前,请查看属性数据类型、构造函数参数和继承子句。

AI助手分析您的表格并建议对象类型和属性映射。ping以及继承层次结构。这项机器学习审查会在对象模型投入生产环境之前标记出冗余属性、缺失的构造函数和过深的继承,从而提高可维护性。

总结一下这篇文章: