【PL/SQL】オブジェクト型とメソッドを使ったオブジェクト指向設計

【PL/SQL】オブジェクト型とメソッドを使ったオブジェクト指向設計 PL/SQL

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でもオブジェクト指向的な設計が可能になります。
属性とメソッドをまとめて定義することで、データと処理の一貫性を保ち、再利用性や保守性を高められます。
一方で、構造変更時の再コンパイルや性能面には注意が必要です。
ビジネスロジックを「データ+振る舞い」として整理する際には、オブジェクト型は非常に強力な選択肢となります。