PL/SQLでもオブジェクト指向的な設計が可能であり、`OBJECT TYPE`を利用することでデータ構造とメソッドをひとつにまとめた「カプセル化」を実現できます。特に業務ロジックをデータごとに振る舞いとして表現したい場合や、再利用性の高いAPIを設計したい場合に効果的です。この記事では、オブジェクト型の定義方法からメソッド実装、継承やコレクションとの組み合わせまでを実例付きで紹介します。
オブジェクト型の基本構造
オブジェクト型(Object Type)は、属性(プロパティ)とメソッド(関数・手続き)を一体で定義できるユーザー定義データ型です。
まずはシンプルな例として「顧客」を表すオブジェクトを作成してみましょう。
-- オブジェクト型の定義
CREATE OR REPLACE TYPE customer_obj AS OBJECT (
cust_id NUMBER,
cust_name VARCHAR2(100),
email VARCHAR2(200),
MEMBER PROCEDURE show_info, -- メンバープロシージャ
MEMBER FUNCTION domain RETURN VARCHAR2 -- メンバーファンクション
);
/
-- オブジェクト本体の実装
CREATE OR REPLACE TYPE BODY customer_obj AS
MEMBER PROCEDURE show_info IS
BEGIN
DBMS_OUTPUT.PUT_LINE('ID: ' || cust_id || ', Name: ' || cust_name || ', Email: ' || email);
END;
MEMBER FUNCTION domain RETURN VARCHAR2 IS
BEGIN
RETURN SUBSTR(email, INSTR(email, '@') + 1);
END;
END;
/
このように、オブジェクト型は型定義(Specification)と実装(Body)で構成され、まるでパッケージのように分離して管理できます。
オブジェクト型の生成と利用
作成したオブジェクト型は、SQLやPL/SQL内でインスタンス化して利用できます。
通常のレコード型と異なり、メソッドを直接呼び出せるのが特徴です。
DECLARE
v_cust customer_obj;
BEGIN
v_cust := customer_obj(101, '佐藤 太郎', 'taro@example.com');
v_cust.show_info;
DBMS_OUTPUT.PUT_LINE('ドメイン名: ' || v_cust.domain);
END;
/
出力結果:
ID: 101, Name: 佐藤 太郎, Email: taro@example.com
ドメイン名: example.com
このように、オブジェクト型はPL/SQLの中で一つの“オブジェクト”として動作し、状態と振る舞いを統合して扱うことができます。
コレクション(ネスト表)との組み合わせ
オブジェクト型は、集合型(VARRAYやTABLE)としても利用できます。
これにより、配列のように複数のオブジェクトを扱い、FORループなどで処理することが可能になります。
-- オブジェクト型のコレクション定義
CREATE OR REPLACE TYPE customer_tab AS TABLE OF customer_obj;
/
-- コレクションの利用例
DECLARE
v_customers customer_tab := customer_tab();
BEGIN
v_customers.EXTEND(2);
v_customers(1) := customer_obj(1, '田中 花子', 'hanako@abc.jp');
v_customers(2) := customer_obj(2, '山田 次郎', 'jiro@xyz.co.jp');
FOR i IN 1..v_customers.COUNT LOOP
v_customers(i).show_info;
END LOOP;
END;
/
このように、オブジェクト配列を扱うことで「顧客リスト」や「商品カタログ」のような業務データをオブジェクト指向的に表現できます。
オブジェクト型の継承とオーバーライド
Oracle 9i以降では、オブジェクト型の継承(UNDER句)もサポートされています。
これにより、共通機能を持つ基本クラスから派生した特化型を作成できます。
-- 親クラス(基本顧客)
CREATE OR REPLACE TYPE base_customer AS OBJECT (
cust_id NUMBER,
cust_name VARCHAR2(100),
MEMBER FUNCTION get_info RETURN VARCHAR2
) NOT FINAL;
/
CREATE OR REPLACE TYPE BODY base_customer AS
MEMBER FUNCTION get_info RETURN VARCHAR2 IS
BEGIN
RETURN '顧客ID: ' || cust_id || ' 名前: ' || cust_name;
END;
END;
/
-- 子クラス(法人顧客)
CREATE OR REPLACE TYPE corporate_customer UNDER base_customer (
company_name VARCHAR2(200),
OVERRIDING MEMBER FUNCTION get_info RETURN VARCHAR2
);
/
CREATE OR REPLACE TYPE BODY corporate_customer AS
OVERRIDING MEMBER FUNCTION get_info RETURN VARCHAR2 IS
BEGIN
RETURN '法人顧客: ' || company_name || ' (' || cust_name || ')';
END;
END;
/
実行例:
DECLARE
v_corp corporate_customer;
BEGIN
v_corp := corporate_customer(2001, '担当者: 鈴木', 'テックソリューション株式会社');
DBMS_OUTPUT.PUT_LINE(v_corp.get_info);
END;
/
-- 出力: 法人顧客: テックソリューション株式会社 (担当者: 鈴木)
このように、親クラスのメソッドをオーバーライドすることで、派生型に固有の振る舞いを実現できます。
テーブル定義とオブジェクト型の組み合わせ
オブジェクト型は、テーブルのカラム定義としても利用できます。
オブジェクト型の列を持つテーブルを作ることで、複合構造をネイティブに扱うことが可能です。
CREATE TABLE order_master (
order_id NUMBER PRIMARY KEY,
customer customer_obj
);
-- 挿入例
INSERT INTO order_master VALUES (1, customer_obj(100, '高橋 一郎', 'ichi@sample.jp'));
-- 参照例
SELECT o.order_id, o.customer.cust_name, o.customer.email
FROM order_master o;
このように、オブジェクト型を使うと、RDBの1行にオブジェクト全体を埋め込むことができ、データモデルをより直感的に表現できます。
オブジェクト型を使う際の注意点
– **オブジェクト型の変更は再作成が必要**
既存テーブルで使用している場合、依存関係を考慮して再コンパイルやデータ移行が必要になります。
– **パフォーマンスへの影響**
大量データ処理には向かないケースがあります。性能要件に応じて通常のテーブル構造と使い分けましょう。
– **デシリアライズ・JSON連携**
`TO_JSON`・`FROM_JSON`などの関数を組み合わせることで、API連携に活用することも可能です(Oracle 19c以降)。
まとめ
オブジェクト型を使うことで、PL/SQLでもオブジェクト指向的な設計が可能になります。
属性とメソッドをまとめて定義することで、データと処理の一貫性を保ち、再利用性や保守性を高められます。
一方で、構造変更時の再コンパイルや性能面には注意が必要です。
ビジネスロジックを「データ+振る舞い」として整理する際には、オブジェクト型は非常に強力な選択肢となります。