在传统的Oracle数据库开发中,我们更多使用关系型表结构存储数据,但面对复杂业务逻辑时,关系模型往往难以直观映射业务实体。其实Oracle很早就支持面向对象编程特性,通过对象类型机制可以让数据库层面的设计更贴合业务实体的抽象。

Oracle面向对象编程的核心:对象类型
对象类型是Oracle实现面向对象的基础,它类似其他编程语言中的类,可以封装属性(对应类的成员变量)和方法(对应类的成员函数/过程)。我们可以把业务中的实体抽象为对象类型,比如定义一个人员对象类型,包含姓名、年龄等属性,以及获取信息、更新信息等方法。
1. 创建基础对象类型
使用CREATE TYPE语句可以创建对象类型,属性需要指定数据类型,方法需要在类型体内定义实现。下面是一个简单的人员对象类型示例:
-- 创建人员对象类型规范,定义属性和方法声明
CREATE OR REPLACE TYPE person_type AS OBJECT (
p_name VARCHAR2(50), -- 姓名属性
p_age NUMBER, -- 年龄属性
-- 方法声明:获取人员信息
MEMBER FUNCTION get_info RETURN VARCHAR2,
-- 方法声明:更新年龄
MEMBER PROCEDURE update_age(p_new_age NUMBER)
);
/
-- 创建人员对象类型体,实现声明的方法
CREATE OR REPLACE TYPE BODY person_type AS
-- 实现获取信息方法
MEMBER FUNCTION get_info RETURN VARCHAR2 IS
BEGIN
RETURN '姓名:' || p_name || ',年龄:' || p_age;
END get_info;
-- 实现更新年龄方法
MEMBER PROCEDURE update_age(p_new_age NUMBER) IS
BEGIN
-- 注意:对象类型的属性在方法内可以直接访问
p_age := p_new_age;
END update_age;
END;
/2. 使用对象类型
创建好对象类型后,我们可以像使用内置数据类型一样使用它,既可以作为表的列类型,也可以单独声明变量使用:
-- 声明对象类型变量并使用
DECLARE
v_person person_type; -- 声明人员对象变量
BEGIN
-- 初始化对象,调用构造函数(默认构造函数参数顺序和属性定义顺序一致)
v_person := person_type('张三', 25);
-- 调用对象的方法
DBMS_OUTPUT.PUT_LINE(v_person.get_info());
-- 调用更新方法
v_person.update_age(26);
DBMS_OUTPUT.PUT_LINE('更新后:' || v_person.get_info());
END;
/
-- 创建包含对象类型列的表
CREATE TABLE employee_tab (
emp_id NUMBER,
emp_info person_type -- 员工信息列使用person_type类型
);
-- 向表中插入数据
INSERT INTO employee_tab VALUES (1, person_type('李四', 30));
COMMIT;
-- 查询对象类型列的数据
SELECT emp_id, e.emp_info.p_name AS 姓名, e.emp_info.p_age AS 年龄
FROM employee_tab e;对象类型的继承特性
Oracle的对象类型支持继承,我们可以创建父类型,再基于父类型扩展出子类型,子类型会继承父类型的所有属性和方法,还可以新增自己的属性和方法。使用UNDER关键字可以定义子类型:
-- 先创建父类型:基础人员类型
CREATE OR REPLACE TYPE base_person_type AS OBJECT (
p_name VARCHAR2(50),
p_age NUMBER,
MEMBER FUNCTION get_base_info RETURN VARCHAR2
) NOT FINAL; -- NOT FINAL表示允许该类型被继承
/
CREATE OR REPLACE TYPE BODY base_person_type AS
MEMBER FUNCTION get_base_info RETURN VARCHAR2 IS
BEGIN
RETURN '姓名:' || p_name || ',年龄:' || p_age;
END get_base_info;
END;
/
-- 创建子类型:员工类型,继承基础人员类型
CREATE OR REPLACE TYPE employee_type UNDER base_person_type (
emp_no VARCHAR2(20), -- 新增员工工号属性
dept_name VARCHAR2(50), -- 新增部门属性
-- 新增方法:获取员工完整信息
OVERRIDING MEMBER FUNCTION get_base_info RETURN VARCHAR2 -- OVERRIDING表示重写父类方法
);
/
CREATE OR REPLACE TYPE BODY employee_type AS
OVERRIDING MEMBER FUNCTION get_base_info RETURN VARCHAR2 IS
BEGIN
-- 可以调用父类的方法,也可以自定义实现
RETURN '工号:' || emp_no || ',姓名:' || p_name || ',年龄:' || p_age || ',部门:' || dept_name;
END get_base_info;
END;
/
-- 使用子类型测试
DECLARE
v_emp employee_type;
BEGIN
v_emp := employee_type('王五', 28, 'E001', '技术部');
DBMS_OUTPUT.PUT_LINE(v_emp.get_base_info());
END;
/面向对象编程在Oracle中的适用场景
并不是所有场景都适合在Oracle中使用面向对象编程,以下场景可以考虑使用:
- 业务实体逻辑复杂,需要把属性和相关操作封装在一起,避免逻辑分散在多个存储过程中
- 多个表或业务模块需要复用相同的实体定义和逻辑,通过对象类型可以减少重复代码
- 需要模拟业务实体的继承关系,比如不同角色的用户有共同的属性,也有各自的特有属性
需要注意的是,Oracle的面向对象特性虽然强大,但过度使用可能会导致数据库设计复杂度上升,查询性能也可能受到影响,实际使用中需要结合业务场景权衡选择。